Highlight selected sequence diagram items.
[metze/wireshark/wip.git] / ui / qt / main_window.cpp
1 /* main_window.cpp
2  *
3  * $Id$
4  *
5  * Wireshark - Network traffic analyzer
6  * By Gerald Combs <gerald@wireshark.org>
7  * Copyright 1998 Gerald Combs
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23
24 #include "main_window.h"
25 #include "ui_main_window.h"
26
27 #include "globals.h"
28
29 #include <epan/epan_dissect.h>
30 #include <epan/filesystem.h>
31 #include <epan/ipproto.h>
32 #include <epan/prefs.h>
33
34 //#include <wiretap/wtap.h>
35
36 #ifdef HAVE_LIBPCAP
37 #include "capture.h"
38 #include "capture-pcap-util.h"
39 #include "capture_ui_utils.h"
40 #include "capture_session.h"
41 #endif
42
43 #include "ui/alert_box.h"
44 #include "ui/capture_globals.h"
45 #include "ui/main_statusbar.h"
46 #include "ui/recent.h"
47 #include "ui/util.h"
48
49 #include "wireshark_application.h"
50 #include "proto_tree.h"
51 #include "byte_view_tab.h"
52 #include "display_filter_edit.h"
53 #include "import_text_dialog.h"
54 #include "export_dissection_dialog.h"
55
56 #include "qt_ui_utils.h"
57
58 #include <QAction>
59 #include <QDesktopWidget>
60 #include <QKeyEvent>
61 #include <QMessageBox>
62 #include <QMetaObject>
63 #include <QPropertyAnimation>
64 #include <QTabWidget>
65 #include <QToolButton>
66 #include <QTreeWidget>
67
68 #ifdef QT_MACEXTRAS_LIB
69 #include <QtMacExtras/QMacNativeToolBar>
70 #endif
71
72 #include <QDebug>
73
74 //menu_recent_file_write_all
75
76 // If we ever add support for multiple windows this will need to be replaced.
77 static MainWindow *gbl_cur_main_window = NULL;
78
79 void pipe_input_set_handler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
80 {
81     gbl_cur_main_window->setPipeInputHandler(source, user_data, child_process, input_cb);
82 }
83
84 MainWindow::MainWindow(QWidget *parent) :
85     QMainWindow(parent),
86     main_ui_(new Ui::MainWindow),
87     df_combo_box_(new DisplayFilterCombo()),
88     cap_file_(NULL),
89     previous_focus_(NULL),
90     capture_stopping_(false),
91     capture_filter_valid_(false),
92 #ifdef _WIN32
93     pipe_timer_(NULL)
94 #else
95     pipe_notifier_(NULL)
96 #endif
97 {
98     gbl_cur_main_window = this;
99     main_ui_->setupUi(this);
100     setTitlebarForCaptureFile();
101     setMenusForCaptureFile();
102     setForCapturedPackets(false);
103     setMenusForSelectedPacket();
104     setMenusForSelectedTreeRow();
105     setForCaptureInProgress(false);
106     setMenusForFileSet(false);
107     interfaceSelectionChanged();
108
109     //To prevent users use features before initialization complete
110     //Otherwise unexpected problems may occur
111     setFeaturesEnabled(false);
112     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(setFeaturesEnabled()));
113
114     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutPanes()));
115
116     connect(wsApp, SIGNAL(recentFilesRead()), this, SLOT(loadWindowGeometry()));
117
118     connect(wsApp, SIGNAL(updateRecentItemStatus(const QString &, qint64, bool)), this, SLOT(updateRecentFiles()));
119     updateRecentFiles();
120
121     connect(&summary_dialog_, SIGNAL(captureCommentChanged()), this, SLOT(updateForUnsavedChanges()));
122
123     const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit());
124     connect(df_edit, SIGNAL(pushFilterSyntaxStatus(QString&)), main_ui_->statusBar, SLOT(pushFilterStatus(QString&)));
125     connect(df_edit, SIGNAL(popFilterSyntaxStatus()), main_ui_->statusBar, SLOT(popFilterStatus()));
126     connect(df_edit, SIGNAL(pushFilterSyntaxWarning(QString&)),
127             main_ui_->statusBar, SLOT(pushTemporaryStatus(QString&)));
128     connect(df_edit, SIGNAL(filterPackets(QString&,bool)), this, SLOT(filterPackets(QString&,bool)));
129     connect(df_edit, SIGNAL(addBookmark(QString)), this, SLOT(addDisplayFilterButton(QString)));
130     connect(this, SIGNAL(displayFilterSuccess(bool)), df_edit, SLOT(displayFilterSuccess(bool)));
131
132     // http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
133     // http://qt-project.org/doc/qt-4.8/qstyle.html#StandardPixmap-enum
134     main_ui_->actionFileOpen->setIcon(
135                 QIcon().fromTheme("document-open", style()->standardIcon(QStyle::SP_DirIcon)));
136     // main_ui_->actionFileSave set in main_window.ui
137     main_ui_->actionFileClose->setIcon(
138                 QIcon().fromTheme("process-stop", style()->standardIcon(QStyle::SP_DialogCloseButton)));
139
140     // In Qt4 multiple toolbars and "pretty" are mutually exculsive on OS X. If
141     // unifiedTitleAndToolBarOnMac is enabled everything ends up in the same row.
142     // https://bugreports.qt-project.org/browse/QTBUG-22433
143     // This property is obsolete in Qt5 so this issue may be fixed in that version.
144     main_ui_->displayFilterToolBar->addWidget(df_combo_box_);
145
146     main_ui_->goToFrame->hide();
147     // XXX For some reason the cursor is drawn funny with an input mask set
148     // https://bugreports.qt-project.org/browse/QTBUG-7174
149
150     main_ui_->searchFrame->hide();
151     connect(main_ui_->searchFrame, SIGNAL(pushFilterSyntaxStatus(QString&)),
152             main_ui_->statusBar, SLOT(pushTemporaryStatus(QString&)));
153
154 #ifndef HAVE_LIBPCAP
155     main_ui_->menuCapture->setEnabled(false);
156 #endif
157
158 #if defined(Q_OS_MAC)
159 #ifdef QT_MACEXTRAS_LIB
160     QMacNativeToolBar *ntb = QtMacExtras::setNativeToolBar(main_ui_->mainToolBar);
161     ntb->setIconSize(QSize(24, 24));
162 #endif // QT_MACEXTRAS_LIB
163
164     foreach (QMenu *menu, main_ui_->menuBar->findChildren<QMenu*>()) {
165         foreach (QAction *act, menu->actions()) {
166             act->setIconVisibleInMenu(false);
167         }
168     }
169     main_ui_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true);
170     main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true);
171     main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true);
172
173     main_ui_->actionEditPreferences->setMenuRole(QAction::PreferencesRole);
174
175 #endif // Q_OS_MAC
176
177 #ifdef HAVE_SOFTWARE_UPDATE
178     QAction *update_sep = main_ui_->menuHelp->insertSeparator(main_ui_->actionHelpAbout);
179     QAction *update_action = new QAction(tr("Check for Updates..."), main_ui_->menuHelp);
180     main_ui_->menuHelp->insertAction(update_sep, update_action);
181     connect(update_action, SIGNAL(triggered()), this, SLOT(on_actionHelpCheckForUpdates_triggered()));
182 #endif
183     master_split_.setObjectName(tr("splitterMaster"));
184     extra_split_.setObjectName(tr("splitterExtra"));
185     main_ui_->mainStack->addWidget(&master_split_);
186
187     empty_pane_.setObjectName(tr("emptyPane"));
188
189     packet_list_ = new PacketList(&master_split_);
190
191     proto_tree_ = new ProtoTree(&master_split_);
192     proto_tree_->setHeaderHidden(true);
193     proto_tree_->installEventFilter(this);
194
195     byte_view_tab_ = new ByteViewTab(&master_split_);
196     byte_view_tab_->setTabPosition(QTabWidget::South);
197     byte_view_tab_->setDocumentMode(true);
198
199     packet_list_->setProtoTree(proto_tree_);
200     packet_list_->setByteViewTab(byte_view_tab_);
201     packet_list_->installEventFilter(this);
202
203     main_welcome_ = main_ui_->welcomePage;
204
205     connect(wsApp, SIGNAL(captureCapturePrepared(capture_session *)),
206             this, SLOT(captureCapturePrepared(capture_session *)));
207     connect(wsApp, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
208             this, SLOT(captureCaptureUpdateStarted(capture_session *)));
209     connect(wsApp, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
210             this, SLOT(captureCaptureUpdateFinished(capture_session *)));
211     connect(wsApp, SIGNAL(captureCaptureFixedStarted(capture_session *)),
212             this, SLOT(captureCaptureFixedStarted(capture_session *)));
213     connect(wsApp, SIGNAL(captureCaptureFixedFinished(capture_session *)),
214             this, SLOT(captureCaptureFixedFinished(capture_session *)));
215     connect(wsApp, SIGNAL(captureCaptureStopping(capture_session *)),
216             this, SLOT(captureCaptureStopping(capture_session *)));
217     connect(wsApp, SIGNAL(captureCaptureFailed(capture_session *)),
218             this, SLOT(captureCaptureFailed(capture_session *)));
219
220     connect(wsApp, SIGNAL(captureFileOpened(const capture_file*)),
221             this, SLOT(captureFileOpened(const capture_file*)));
222     connect(wsApp, SIGNAL(captureFileReadStarted(const capture_file*)),
223             this, SLOT(captureFileReadStarted(const capture_file*)));
224     connect(wsApp, SIGNAL(captureFileReadFinished(const capture_file*)),
225             this, SLOT(captureFileReadFinished(const capture_file*)));
226     connect(wsApp, SIGNAL(captureFileClosing(const capture_file*)),
227             this, SLOT(captureFileClosing(const capture_file*)));
228     connect(wsApp, SIGNAL(captureFileClosed(const capture_file*)),
229             this, SLOT(captureFileClosed(const capture_file*)));
230     connect(wsApp, SIGNAL(columnsChanged()),
231             this, SLOT(recreatePacketList()));
232     connect(wsApp, SIGNAL(packetDissectionChanged()),
233             this, SLOT(redissectPackets()));
234     connect(wsApp, SIGNAL(appInitialized()),
235             this, SLOT(filterExpressionsChanged()));
236     connect(wsApp, SIGNAL(filterExpressionsChanged()),
237             this, SLOT(filterExpressionsChanged()));
238
239     connect(main_welcome_, SIGNAL(startCapture()),
240             this, SLOT(startCapture()));
241     connect(main_welcome_, SIGNAL(recentFileActivated(QString&)),
242             this, SLOT(openCaptureFile(QString&)));
243     connect(main_welcome_, SIGNAL(pushFilterSyntaxStatus(QString&)),
244             main_ui_->statusBar, SLOT(pushFilterStatus(QString&)));
245     connect(main_welcome_, SIGNAL(popFilterSyntaxStatus()),
246             main_ui_->statusBar, SLOT(popFilterStatus()));
247
248     connect(this, SIGNAL(setCaptureFile(capture_file*)),
249             main_ui_->searchFrame, SLOT(setCaptureFile(capture_file*)));
250     connect(this, SIGNAL(setCaptureFile(capture_file*)),
251             main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
252     connect(this, SIGNAL(setCaptureFile(capture_file*)),
253             packet_list_, SLOT(setCaptureFile(capture_file*)));
254     connect(this, SIGNAL(setCaptureFile(capture_file*)),
255             byte_view_tab_, SLOT(setCaptureFile(capture_file*)));
256
257     connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
258             packet_list_, SLOT(goNextPacket()));
259     connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
260             packet_list_, SLOT(goPreviousPacket()));
261     connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
262             packet_list_, SLOT(goFirstPacket()));
263     connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
264             packet_list_, SLOT(goLastPacket()));
265
266     connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
267             proto_tree_, SLOT(expandSubtrees()));
268     connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
269             proto_tree_, SLOT(expandAll()));
270     connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
271             proto_tree_, SLOT(collapseAll()));
272
273     connect(packet_list_->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
274             this, SLOT(setMenusForSelectedPacket()));
275     connect(packet_list_, SIGNAL(packetDissectionChanged()),
276             this, SLOT(redissectPackets()));
277     connect(packet_list_, SIGNAL(setMenusFollowStream()),
278             this, SLOT(setMenusForFollowStream()));
279
280     connect(proto_tree_, SIGNAL(protoItemSelected(QString&)),
281             main_ui_->statusBar, SLOT(pushFieldStatus(QString&)));
282
283     connect(proto_tree_, SIGNAL(protoItemSelected(field_info *)),
284             this, SLOT(setMenusForSelectedTreeRow(field_info *)));
285
286     connect(&file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString&)),
287             this, SLOT(openCaptureFile(QString&)));
288
289     QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
290     if (iface_tree) {
291         connect(iface_tree, SIGNAL(itemSelectionChanged()),
292                 this, SLOT(interfaceSelectionChanged()));
293     }
294     connect(main_ui_->welcomePage, SIGNAL(captureFilterSyntaxChanged(bool)),
295             this, SLOT(captureFilterSyntaxChanged(bool)));
296
297     main_ui_->mainStack->setCurrentWidget(main_welcome_);
298 }
299
300 MainWindow::~MainWindow()
301 {
302     delete main_ui_;
303 }
304
305 QString MainWindow::getFilter()
306 {
307     return df_combo_box_->itemText(df_combo_box_->count());
308 }
309
310 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
311 {
312     pipe_source_        = source;
313     pipe_child_process_ = child_process;
314     pipe_user_data_     = user_data;
315     pipe_input_cb_      = input_cb;
316
317 #ifdef _WIN32
318     /* Tricky to use pipes in win9x, as no concept of wait.  NT can
319        do this but that doesn't cover all win32 platforms.  GTK can do
320        this but doesn't seem to work over processes.  Attempt to do
321        something similar here, start a timer and check for data on every
322        timeout. */
323        /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
324
325     if (pipe_timer_) {
326         disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
327         delete pipe_timer_;
328     }
329
330     pipe_timer_ = new QTimer(this);
331     connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
332     connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
333     pipe_timer_->start(200);
334 #else
335     if (pipe_notifier_) {
336         disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
337         delete pipe_notifier_;
338     }
339
340     pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
341     // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
342     connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
343     connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
344 #endif
345 }
346
347
348 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
349
350     // The user typed some text. Start filling in a filter.
351     // We may need to be more choosy here. We just need to catch events for the packet list,
352     // proto tree, and main welcome widgets.
353     if (event->type() == QEvent::KeyPress) {
354         QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
355         if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
356             df_combo_box_->lineEdit()->insert(kevt->text());
357             df_combo_box_->lineEdit()->setFocus();
358             return true;
359         }
360     }
361
362     return QMainWindow::eventFilter(obj, event);
363 }
364
365 void MainWindow::keyPressEvent(QKeyEvent *event) {
366
367     // Explicitly focus on the display filter combo.
368     if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
369         df_combo_box_->setFocus(Qt::ShortcutFocusReason);
370         return;
371     }
372
373     if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
374         if (event->modifiers() == Qt::NoModifier) {
375             if (event->key() == Qt::Key_Escape) {
376                 on_goToCancel_clicked();
377             } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
378                 on_goToGo_clicked();
379             }
380         }
381         return; // goToLineEdit didn't want it and we don't either.
382     }
383
384     // Move up & down the packet list.
385     if (event->key() == Qt::Key_F7) {
386         packet_list_->goPreviousPacket();
387     } else if (event->key() == Qt::Key_F8) {
388         packet_list_->goNextPacket();
389     }
390
391     // Move along, citizen.
392     QMainWindow::keyPressEvent(event);
393 }
394
395 void MainWindow::closeEvent(QCloseEvent *event) {
396     saveWindowGeometry();
397
398     /* If we're in the middle of stopping a capture, don't do anything;
399        the user can try deleting the window after the capture stops. */
400     if (capture_stopping_) {
401         event->ignore();
402         return;
403     }
404
405     // Make sure we kill any open dumpcap processes.
406     delete main_welcome_;
407
408     if(!wsApp->isInitialized()) {
409         // If we're still initializing, QCoreApplication::quit() won't
410         // exit properly because we are not in the event loop. This
411         // means that the application won't clean up after itself. We
412         // might want to call wsApp->processEvents() during startup
413         // instead so that we can do a normal exit here.
414         exit(0);
415     }
416 }
417
418 const int min_sensible_dimension = 200;
419 const int geom_animation_duration = 150;
420 void MainWindow::loadWindowGeometry()
421 {
422     QWidget shadow_main(wsApp->desktop());
423     shadow_main.setVisible(false);
424
425     // Start off with the Widget defaults
426     shadow_main.restoreGeometry(saveGeometry());
427
428     // Apply any saved settings
429
430     // Note that we're saving and restoring the outer window frame
431     // position and the inner client area size.
432 //    if (prefs.gui_geometry_save_position) {
433         shadow_main.move(recent.gui_geometry_main_x, recent.gui_geometry_main_y);
434 //    }
435
436     // XXX Preferences haven't been loaded at this point. For now we
437     // assume default (true) values for everything.
438
439     if (// prefs.gui_geometry_save_size &&
440             recent.gui_geometry_main_width > min_sensible_dimension &&
441             recent.gui_geometry_main_width > min_sensible_dimension) {
442         shadow_main.resize(recent.gui_geometry_main_width, recent.gui_geometry_main_height);
443     }
444
445     // Let Qt move and resize our window if needed (e.g. if it's offscreen)
446     QByteArray geom = shadow_main.saveGeometry();
447
448 #ifndef Q_OS_MAC
449     if (/* prefs.gui_geometry_save_maximized && */ recent.gui_geometry_main_maximized) {
450         setWindowState(Qt::WindowMaximized);
451     } else
452 #endif
453     if (strlen (get_conn_cfilter()) < 1) {
454         QPropertyAnimation *pos_anim = new QPropertyAnimation(this, "pos");
455         QPropertyAnimation *size_anim = new QPropertyAnimation(this, "size");
456
457         shadow_main.restoreGeometry(geom);
458
459         pos_anim->setDuration(geom_animation_duration);
460         pos_anim->setStartValue(pos());
461         pos_anim->setEndValue(shadow_main.pos());
462         pos_anim->setEasingCurve(QEasingCurve::InOutQuad);
463         size_anim->setDuration(geom_animation_duration);
464         size_anim->setStartValue(size());
465         size_anim->setEasingCurve(QEasingCurve::InOutQuad);
466         size_anim->setEndValue(shadow_main.size());
467
468         pos_anim->start(QAbstractAnimation::DeleteWhenStopped);
469         size_anim->start(QAbstractAnimation::DeleteWhenStopped);
470     } else {
471         restoreGeometry(geom);
472     }
473
474 }
475
476 void MainWindow::saveWindowGeometry()
477 {
478     if (prefs.gui_geometry_save_position) {
479         recent.gui_geometry_main_x = pos().x();
480         recent.gui_geometry_main_y = pos().y();
481     }
482
483     if (prefs.gui_geometry_save_size) {
484         recent.gui_geometry_main_width = size().width();
485         recent.gui_geometry_main_height = size().height();
486     }
487
488     if (prefs.gui_geometry_save_maximized) {
489         // On OS X this is false when it shouldn't be
490         recent.gui_geometry_main_maximized = isMaximized();
491     }
492 }
493
494 QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) {
495     switch (type) {
496         case layout_pane_content_none:
497             return &empty_pane_;
498         case layout_pane_content_plist:
499             return packet_list_;
500         case layout_pane_content_pdetails:
501             return proto_tree_;
502         case layout_pane_content_pbytes:
503             return byte_view_tab_;
504         default:
505             g_assert_not_reached();
506             return NULL;
507     }
508 }
509
510 void MainWindow::mergeCaptureFile()
511 {
512     QString file_name = "";
513     QString display_filter = "";
514     dfilter_t *rfcode = NULL;
515     int err;
516
517     if (!cap_file_)
518         return;
519
520     if (prefs.gui_ask_unsaved) {
521         if (cf_has_unsaved_data(cap_file_)) {
522             QMessageBox msg_dialog;
523             gchar *display_basename;
524             int response;
525
526             msg_dialog.setIcon(QMessageBox::Question);
527             /* This file has unsaved data; ask the user whether to save
528                the capture. */
529             if (cap_file_->is_tempfile) {
530                 msg_dialog.setText(tr("Save packets before merging?"));
531                 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
532             } else {
533                 /*
534                  * Format the message.
535                  */
536                 display_basename = g_filename_display_basename(cap_file_->filename);
537                 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
538                 g_free(display_basename);
539                 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
540             }
541
542             msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
543             msg_dialog.setDefaultButton(QMessageBox::Save);
544
545             response = msg_dialog.exec();
546
547             switch (response) {
548
549             case QMessageBox::Save:
550                 /* Save the file but don't close it */
551                 saveCaptureFile(cap_file_, FALSE);
552                 break;
553
554             case QMessageBox::Cancel:
555             default:
556                 /* Don't do the merge. */
557                 return;
558             }
559         }
560     }
561
562     for (;;) {
563         CaptureFileDialog merge_dlg(this, cap_file_, display_filter);
564         int file_type;
565         cf_status_t  merge_status;
566         char        *in_filenames[2];
567         char        *tmpname;
568
569         switch (prefs.gui_fileopen_style) {
570
571         case FO_STYLE_LAST_OPENED:
572             /* The user has specified that we should start out in the last directory
573            we looked in.  If we've already opened a file, use its containing
574            directory, if we could determine it, as the directory, otherwise
575            use the "last opened" directory saved in the preferences file if
576            there was one. */
577             /* This is now the default behaviour in file_selection_new() */
578             break;
579
580         case FO_STYLE_SPECIFIED:
581             /* The user has specified that we should always start out in a
582            specified directory; if they've specified that directory,
583            start out by showing the files in that dir. */
584             if (prefs.gui_fileopen_dir[0] != '\0')
585                 merge_dlg.setDirectory(prefs.gui_fileopen_dir);
586             break;
587         }
588
589         if (merge_dlg.merge(file_name)) {
590             if (dfilter_compile(display_filter.toUtf8().constData(), &rfcode)) {
591                 cf_set_rfcode(cap_file_, rfcode);
592             } else {
593                 /* Not valid.  Tell the user, and go back and run the file
594                    selection box again once they dismiss the alert. */
595                 //bad_dfilter_alert_box(top_level, display_filter->str);
596                 QMessageBox::warning(this, tr("Invalid Display Filter"),
597                                      QString(tr("The filter expression %1 isn't a valid display filter. (%2).").arg(display_filter, dfilter_error_msg)),
598                                      QMessageBox::Ok);
599                 continue;
600             }
601         } else {
602             return;
603         }
604
605         file_type = cap_file_->cd_t;
606
607         /* Try to merge or append the two files */
608         tmpname = NULL;
609         if (merge_dlg.mergeType() == 0) {
610             /* chronological order */
611             in_filenames[0] = cap_file_->filename;
612             in_filenames[1] = file_name.toUtf8().data();
613             merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
614         } else if (merge_dlg.mergeType() <= 0) {
615             /* prepend file */
616             in_filenames[0] = file_name.toUtf8().data();
617             in_filenames[1] = cap_file_->filename;
618             merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
619         } else {
620             /* append file */
621             in_filenames[0] = cap_file_->filename;
622             in_filenames[1] = file_name.toUtf8().data();
623             merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
624         }
625
626         if (merge_status != CF_OK) {
627             if (rfcode != NULL)
628                 dfilter_free(rfcode);
629             g_free(tmpname);
630             continue;
631         }
632
633         cf_close(cap_file_);
634
635         /* Try to open the merged capture file. */
636         cfile.window = this;
637         if (cf_open(&cfile, tmpname, TRUE /* temporary file */, &err) != CF_OK) {
638             /* We couldn't open it; fail. */
639             cfile.window = NULL;
640             if (rfcode != NULL)
641                 dfilter_free(rfcode);
642             g_free(tmpname);
643             return;
644         }
645
646         /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
647            it closed the previous capture file, and thus destroyed any
648            previous read filter attached to "cf"). */
649         cfile.rfcode = rfcode;
650
651         switch (cf_read(&cfile, FALSE)) {
652
653         case CF_READ_OK:
654         case CF_READ_ERROR:
655             /* Just because we got an error, that doesn't mean we were unable
656              to read any of the file; we handle what we could get from the
657              file. */
658             break;
659
660         case CF_READ_ABORTED:
661             /* The user bailed out of re-reading the capture file; the
662              capture file has been closed - just free the capture file name
663              string and return (without changing the last containing
664              directory). */
665             g_free(tmpname);
666             return;
667         }
668
669         /* Save the name of the containing directory specified in the path name,
670            if any; we can write over cf_merged_name, which is a good thing, given that
671            "get_dirname()" does write over its argument. */
672         wsApp->setLastOpenDir(get_dirname(tmpname));
673         g_free(tmpname);
674         df_combo_box_->setEditText(display_filter);
675         main_ui_->statusBar->showExpert();
676         return;
677     }
678
679 }
680
681 void MainWindow::importCaptureFile() {
682     ImportTextDialog import_dlg;
683
684     if (!testCaptureFileClose(FALSE, *new QString(tr(" before importing a new capture"))))
685         return;
686
687     import_dlg.exec();
688
689     if (import_dlg.result() != QDialog::Accepted) {
690         main_ui_->mainStack->setCurrentWidget(main_welcome_);
691         return;
692     }
693
694     openCaptureFile(import_dlg.capfileName());
695 }
696
697 void MainWindow::saveCaptureFile(capture_file *cf, bool stay_closed) {
698     QString file_name;
699     gboolean discard_comments;
700
701     if (cf->is_tempfile) {
702         /* This is a temporary capture file, so saving it means saving
703            it to a permanent file.  Prompt the user for a location
704            to which to save it.  Don't require that the file format
705            support comments - if it's a temporary capture file, it's
706            probably pcap-ng, which supports comments and, if it's
707            not pcap-ng, let the user decide what they want to do
708            if they've added comments. */
709         saveAsCaptureFile(cf, FALSE, stay_closed);
710     } else {
711         if (cf->unsaved_changes) {
712             cf_write_status_t status;
713
714             /* This is not a temporary capture file, but it has unsaved
715                changes, so saving it means doing a "safe save" on top
716                of the existing file, in the same format - no UI needed
717                unless the file has comments and the file's format doesn't
718                support them.
719
720                If the file has comments, does the file's format support them?
721                If not, ask the user whether they want to discard the comments
722                or choose a different format. */
723             switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
724
725             case SAVE:
726                 /* The file can be saved in the specified format as is;
727                    just drive on and save in the format they selected. */
728                 discard_comments = FALSE;
729                 break;
730
731             case SAVE_WITHOUT_COMMENTS:
732                 /* The file can't be saved in the specified format as is,
733                    but it can be saved without the comments, and the user
734                    said "OK, discard the comments", so save it in the
735                    format they specified without the comments. */
736                 discard_comments = TRUE;
737                 break;
738
739             case SAVE_IN_ANOTHER_FORMAT:
740                 /* There are file formats in which we can save this that
741                    support comments, and the user said not to delete the
742                    comments.  Do a "Save As" so the user can select
743                    one of those formats and choose a file name. */
744                 saveAsCaptureFile(cf, TRUE, stay_closed);
745                 return;
746
747             case CANCELLED:
748                 /* The user said "forget it".  Just return. */
749                 return;
750
751             default:
752                 /* Squelch warnings that discard_comments is being used
753                    uninitialized. */
754                 g_assert_not_reached();
755                 return;
756             }
757
758             /* XXX - cf->filename might get freed out from under us, because
759                the code path through which cf_save_packets() goes currently
760                closes the current file and then opens and reloads the saved file,
761                so make a copy and free it later. */
762             file_name = cf->filename;
763             status = cf_save_packets(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
764                                      discard_comments, stay_closed);
765             switch (status) {
766
767             case CF_WRITE_OK:
768                 /* The save succeeded; we're done.
769                    If we discarded comments, redraw the packet list to reflect
770                    any packets that no longer have comments. */
771                 if (discard_comments)
772                     packet_list_queue_draw();
773
774                 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
775                 updateForUnsavedChanges(); // we update the title bar to remove the *
776                 break;
777
778             case CF_WRITE_ERROR:
779                 /* The write failed.
780                    XXX - OK, what do we do now?  Let them try a
781                    "Save As", in case they want to try to save to a
782                    different directory r file system? */
783                 break;
784
785             case CF_WRITE_ABORTED:
786                 /* The write was aborted; just drive on. */
787                 break;
788             }
789         }
790         /* Otherwise just do nothing. */
791     }
792 }
793
794 void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool stay_closed) {
795     QString file_name = "";
796     int file_type;
797     gboolean compressed;
798     cf_write_status_t status;
799     gchar   *dirname;
800     gboolean discard_comments = FALSE;
801
802     if (!cf) {
803         return;
804     }
805
806     for (;;) {
807         CaptureFileDialog save_as_dlg(this, cf);
808
809         switch (prefs.gui_fileopen_style) {
810
811         case FO_STYLE_LAST_OPENED:
812             /* The user has specified that we should start out in the last directory
813                we looked in.  If we've already opened a file, use its containing
814                directory, if we could determine it, as the directory, otherwise
815                use the "last opened" directory saved in the preferences file if
816                there was one. */
817             /* This is now the default behaviour in file_selection_new() */
818             break;
819
820         case FO_STYLE_SPECIFIED:
821             /* The user has specified that we should always start out in a
822                specified directory; if they've specified that directory,
823                start out by showing the files in that dir. */
824             if (prefs.gui_fileopen_dir[0] != '\0')
825                 save_as_dlg.setDirectory(prefs.gui_fileopen_dir);
826             break;
827         }
828
829         /* If the file has comments, does the format the user selected
830            support them?  If not, ask the user whether they want to
831            discard the comments or choose a different format. */
832         switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
833
834         case SAVE:
835             /* The file can be saved in the specified format as is;
836                just drive on and save in the format they selected. */
837             discard_comments = FALSE;
838             break;
839
840         case SAVE_WITHOUT_COMMENTS:
841             /* The file can't be saved in the specified format as is,
842                but it can be saved without the comments, and the user
843                said "OK, discard the comments", so save it in the
844                format they specified without the comments. */
845             discard_comments = TRUE;
846             break;
847
848         case SAVE_IN_ANOTHER_FORMAT:
849             /* There are file formats in which we can save this that
850                support comments, and the user said not to delete the
851                comments.  The combo box of file formats has had the
852                formats that don't support comments trimmed from it,
853                so run the dialog again, to let the user decide
854                whether to save in one of those formats or give up. */
855             discard_comments = FALSE;
856             must_support_comments = TRUE;
857             continue;
858
859         case CANCELLED:
860             /* The user said "forget it".  Just get rid of the dialog box
861                and return. */
862             return;
863         }
864         file_type = save_as_dlg.selectedFileType();
865         compressed = save_as_dlg.isCompressed();
866
867         fileAddExtension(file_name, file_type, compressed);
868
869 //#ifndef _WIN32
870 //        /* If the file exists and it's user-immutable or not writable,
871 //                       ask the user whether they want to override that. */
872 //        if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
873 //            /* They don't.  Let them try another file name or cancel. */
874 //            continue;
875 //        }
876 //#endif
877
878         /* Attempt to save the file */
879         status = cf_save_packets(cf, file_name.toUtf8().constData(), file_type, compressed,
880                                  discard_comments, stay_closed);
881         switch (status) {
882
883         case CF_WRITE_OK:
884             /* The save succeeded; we're done. */
885             /* Save the directory name for future file dialogs. */
886             dirname = get_dirname(file_name.toUtf8().data());  /* Overwrites cf_name */
887             set_last_open_dir(dirname);
888             /* If we discarded comments, redraw the packet list to reflect
889                any packets that no longer have comments. */
890             if (discard_comments)
891                 packet_list_queue_draw();
892
893             cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
894             updateForUnsavedChanges(); // we update the title bar to remove the *
895             return;
896
897         case CF_WRITE_ERROR:
898             /* The save failed; let the user try again. */
899             continue;
900
901         case CF_WRITE_ABORTED:
902             /* The user aborted the save; just return. */
903             return;
904         }
905     }
906     return;
907 }
908
909 void MainWindow::exportSelectedPackets() {
910     QString file_name = "";
911     int file_type;
912     gboolean compressed;
913     packet_range_t range;
914     cf_write_status_t status;
915     gchar   *dirname;
916     gboolean discard_comments = FALSE;
917
918     if (!cap_file_)
919         return;
920
921     /* Init the packet range */
922     packet_range_init(&range, cap_file_);
923     range.process_filtered = TRUE;
924     range.include_dependents = TRUE;
925
926     for (;;) {
927         CaptureFileDialog esp_dlg(this, cap_file_);
928
929         switch (prefs.gui_fileopen_style) {
930
931         case FO_STYLE_LAST_OPENED:
932             /* The user has specified that we should start out in the last directory
933                we looked in.  If we've already opened a file, use its containing
934                directory, if we could determine it, as the directory, otherwise
935                use the "last opened" directory saved in the preferences file if
936                there was one. */
937             /* This is now the default behaviour in file_selection_new() */
938             break;
939
940         case FO_STYLE_SPECIFIED:
941             /* The user has specified that we should always start out in a
942                specified directory; if they've specified that directory,
943                start out by showing the files in that dir. */
944             if (prefs.gui_fileopen_dir[0] != '\0')
945                 esp_dlg.setDirectory(prefs.gui_fileopen_dir);
946             break;
947         }
948
949         /* If the file has comments, does the format the user selected
950            support them?  If not, ask the user whether they want to
951            discard the comments or choose a different format. */
952         switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
953
954         case SAVE:
955             /* The file can be saved in the specified format as is;
956                just drive on and save in the format they selected. */
957             discard_comments = FALSE;
958             break;
959
960         case SAVE_WITHOUT_COMMENTS:
961             /* The file can't be saved in the specified format as is,
962                but it can be saved without the comments, and the user
963                said "OK, discard the comments", so save it in the
964                format they specified without the comments. */
965             discard_comments = TRUE;
966             break;
967
968         case SAVE_IN_ANOTHER_FORMAT:
969             /* There are file formats in which we can save this that
970                support comments, and the user said not to delete the
971                comments.  The combo box of file formats has had the
972                formats that don't support comments trimmed from it,
973                so run the dialog again, to let the user decide
974                whether to save in one of those formats or give up. */
975             discard_comments = FALSE;
976             continue;
977
978         case CANCELLED:
979             /* The user said "forget it".  Just get rid of the dialog box
980                and return. */
981             return;
982         }
983
984         /*
985          * Check that we're not going to save on top of the current
986          * capture file.
987          * We do it here so we catch all cases ...
988          * Unfortunately, the file requester gives us an absolute file
989          * name and the read file name may be relative (if supplied on
990          * the command line). From Joerg Mayer.
991          */
992         if (files_identical(cap_file_->filename, file_name.toUtf8().constData())) {
993             QMessageBox msg_box;
994             gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
995
996             msg_box.setIcon(QMessageBox::Critical);
997             msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
998             msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
999             msg_box.setStandardButtons(QMessageBox::Ok);
1000             msg_box.setDefaultButton(QMessageBox::Ok);
1001             msg_box.exec();
1002             g_free(display_basename);
1003             continue;
1004         }
1005
1006         file_type = esp_dlg.selectedFileType();
1007         compressed = esp_dlg.isCompressed();
1008         fileAddExtension(file_name, file_type, compressed);
1009
1010 //#ifndef _WIN32
1011 //        /* If the file exists and it's user-immutable or not writable,
1012 //                       ask the user whether they want to override that. */
1013 //        if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1014 //            /* They don't.  Let them try another file name or cancel. */
1015 //            continue;
1016 //        }
1017 //#endif
1018
1019         /* Attempt to save the file */
1020         status = cf_export_specified_packets(cap_file_, file_name.toUtf8().constData(), &range, file_type, compressed);
1021         switch (status) {
1022
1023         case CF_WRITE_OK:
1024             /* The save succeeded; we're done. */
1025             /* Save the directory name for future file dialogs. */
1026             dirname = get_dirname(file_name.toUtf8().data());  /* Overwrites cf_name */
1027             set_last_open_dir(dirname);
1028             /* If we discarded comments, redraw the packet list to reflect
1029                any packets that no longer have comments. */
1030             if (discard_comments)
1031                 packet_list_queue_draw();
1032             return;
1033
1034         case CF_WRITE_ERROR:
1035             /* The save failed; let the user try again. */
1036             continue;
1037
1038         case CF_WRITE_ABORTED:
1039             /* The user aborted the save; just return. */
1040             return;
1041         }
1042     }
1043     return;
1044 }
1045
1046 void MainWindow::exportDissections(export_type_e export_type) {
1047     ExportDissectionDialog ed_dlg(this, cap_file_, export_type);
1048     packet_range_t range;
1049
1050     if (!cap_file_)
1051         return;
1052
1053     /* Init the packet range */
1054     packet_range_init(&range, cap_file_);
1055     range.process_filtered = TRUE;
1056     range.include_dependents = TRUE;
1057
1058     ed_dlg.exec();
1059 }
1060
1061 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
1062     QString file_name_lower;
1063     QString file_suffix;
1064     GSList  *extensions_list;
1065     gboolean add_extension;
1066
1067     /*
1068      * Append the default file extension if there's none given by
1069      * the user or if they gave one that's not one of the valid
1070      * extensions for the file type.
1071      */
1072     file_name_lower = file_name.toLower();
1073     extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
1074     if (extensions_list != NULL) {
1075         GSList *extension;
1076
1077         /* We have one or more extensions for this file type.
1078            Start out assuming we need to add the default one. */
1079         add_extension = TRUE;
1080
1081         /* OK, see if the file has one of those extensions. */
1082         for (extension = extensions_list; extension != NULL;
1083              extension = g_slist_next(extension)) {
1084             file_suffix += tr(".") + (char *)extension->data;
1085             if (file_name_lower.endsWith(file_suffix)) {
1086                 /*
1087                  * The file name has one of the extensions for
1088                  * this file type.
1089                  */
1090                 add_extension = FALSE;
1091                 break;
1092             }
1093             file_suffix += ".gz";
1094             if (file_name_lower.endsWith(file_suffix)) {
1095                 /*
1096                  * The file name has one of the extensions for
1097                  * this file type.
1098                  */
1099                 add_extension = FALSE;
1100                 break;
1101             }
1102         }
1103     } else {
1104         /* We have no extensions for this file type.  Don't add one. */
1105         add_extension = FALSE;
1106     }
1107     if (add_extension) {
1108         if (wtap_default_file_extension(file_type) != NULL) {
1109             file_name += tr(".") + wtap_default_file_extension(file_type);
1110             if (compressed) {
1111                 file_name += ".gz";
1112             }
1113         }
1114     }
1115 }
1116
1117 bool MainWindow::testCaptureFileClose(bool from_quit, QString &before_what) {
1118     bool capture_in_progress = FALSE;
1119
1120     if (!cap_file_ || cap_file_->state == FILE_CLOSED)
1121         return true; /* Already closed, nothing to do */
1122
1123 #ifdef HAVE_LIBPCAP
1124     if (cap_file_->state == FILE_READ_IN_PROGRESS) {
1125         /* This is true if we're reading a capture file *or* if we're doing
1126          a live capture.  If we're reading a capture file, the main loop
1127          is busy reading packets, and only accepting input from the
1128          progress dialog, so we can't get here, so this means we're
1129          doing a capture. */
1130         capture_in_progress = TRUE;
1131     }
1132 #endif
1133
1134     if (prefs.gui_ask_unsaved) {
1135         if (cf_has_unsaved_data(cap_file_) || capture_in_progress) {
1136             QMessageBox msg_dialog;
1137             QString question;
1138             QPushButton *saveButton;
1139             QPushButton *discardButton;
1140
1141             msg_dialog.setIcon(QMessageBox::Question);
1142
1143             /* This file has unsaved data or there's a capture in
1144                progress; ask the user whether to save the data. */
1145             if (cap_file_->is_tempfile) {
1146
1147                 msg_dialog.setText(tr("You have unsaved packets"));
1148                 msg_dialog.setInformativeText(tr("They will be lost if you don't save them."));
1149
1150                 if (capture_in_progress) {
1151                     question.append(tr("Do you want to stop the capture and save the captured packets"));
1152                 } else {
1153                     question.append(tr("Do you want to save the captured packets"));
1154                 }
1155                 question.append(before_what).append(tr("?"));
1156                 msg_dialog.setInformativeText(question);
1157
1158
1159             } else {
1160                 /*
1161                  * Format the message.
1162                  */
1163                 if (capture_in_progress) {
1164                     question.append(tr("Do you want to stop the capture and save the captured packets"));
1165                     question.append(before_what).append(tr("?"));
1166                     msg_dialog.setInformativeText(tr("Your captured packets will be lost if you don't save them."));
1167                 } else {
1168                     gchar *display_basename = g_filename_display_basename(cap_file_->filename);
1169                     question.append(QString(tr("Do you want to save the changes you've made to the capture file \"%1\"%2?"))
1170                                     .arg(display_basename)
1171                                     .arg(before_what)
1172                                     );
1173                     g_free(display_basename);
1174                     msg_dialog.setInformativeText(tr("Your changes will be lost if you don't save them."));
1175                 }
1176             }
1177
1178             // XXX Text comes from ui/gtk/stock_icons.[ch]
1179             // Note that the button roles differ from the GTK+ version.
1180             // Cancel = RejectRole
1181             // Save = AcceptRole
1182             // Don't Save = DestructiveRole
1183             msg_dialog.addButton(QMessageBox::Cancel);
1184
1185             if (capture_in_progress) {
1186                 saveButton = msg_dialog.addButton(tr("Stop and Save"), QMessageBox::AcceptRole);
1187             } else {
1188                 saveButton = msg_dialog.addButton(QMessageBox::Save);
1189             }
1190             msg_dialog.setDefaultButton(saveButton);
1191
1192             if (from_quit) {
1193                 if (cap_file_->state == FILE_READ_IN_PROGRESS) {
1194                     discardButton = msg_dialog.addButton(tr("Stop and Quit without Saving"),
1195                                                          QMessageBox::DestructiveRole);
1196                 } else {
1197                     discardButton = msg_dialog.addButton(tr("Quit without Saving"),
1198                                                          QMessageBox::DestructiveRole);
1199                 }
1200             } else {
1201                 if (capture_in_progress) {
1202                     discardButton = msg_dialog.addButton(tr("Stop and Continue without Saving"),
1203                                                          QMessageBox::DestructiveRole);
1204                 } else {
1205                     discardButton = msg_dialog.addButton(QMessageBox::Discard);
1206                 }
1207             }
1208
1209             msg_dialog.exec();
1210             /* According to the Qt doc:
1211              * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1212              *
1213              * Therefore we should use clickedButton() to determine which button was clicked. */
1214
1215             if(msg_dialog.clickedButton() == saveButton)
1216             {
1217 #ifdef HAVE_LIBPCAP
1218                 /* If there's a capture in progress, we have to stop the capture
1219              and then do the save. */
1220                 if (capture_in_progress)
1221                     captureStop();
1222 #endif
1223                 /* Save the file and close it */
1224                 saveCaptureFile(cap_file_, TRUE);
1225             }
1226             else if(msg_dialog.clickedButton() == discardButton)
1227             {
1228 #ifdef HAVE_LIBPCAP
1229                 /*
1230                  * If there's a capture in progress; we have to stop the capture
1231                  * and then do the close.
1232                  */
1233                 if (capture_in_progress)
1234                     captureStop();
1235 #endif
1236                 /* Just close the file, discarding changes */
1237                 cf_close(cap_file_);
1238                 return true;
1239             }
1240             else    //cancelButton or some other unspecified button
1241             {
1242                 return false;
1243             }
1244
1245         } else {
1246             /* Unchanged file, just close it */
1247             cf_close(cap_file_);
1248         }
1249     } else {
1250         /* User asked not to be bothered by those prompts, just close it.
1251          XXX - should that apply only to saving temporary files? */
1252 #ifdef HAVE_LIBPCAP
1253         /* If there's a capture in progress, we have to stop the capture
1254            and then do the close. */
1255         if (capture_in_progress)
1256             captureStop();
1257 #endif
1258         cf_close(cap_file_);
1259     }
1260
1261     return true; /* File closed */
1262 }
1263
1264 void MainWindow::captureStop() {
1265     stopCapture();
1266
1267     while(cap_file_ && cap_file_->state == FILE_READ_IN_PROGRESS) {
1268         WiresharkApplication::processEvents();
1269     }
1270 }
1271
1272 // Titlebar
1273 void MainWindow::setTitlebarForCaptureFile()
1274 {
1275     if (cap_file_ && cap_file_->filename) {
1276         //
1277         // Qt *REALLY* doesn't like windows that sometimes have a
1278         // title set with setWindowTitle() and other times have a
1279         // file path set; apparently, once you've set the title
1280         // with setWindowTitle(), it sticks, and setWindowFilePath()
1281         // has no effect.  It appears to can clear the title with
1282         // setWindowTitle(NULL), but that clears the actual title in
1283         // the title bar, and setWindowFilePath() then, I guess, sees
1284         // that there's already a file path, and does nothing, leaving
1285         // the title bar empty.  So you then have to clear the file path
1286         // with setWindowFilePath(NULL), and then set it.
1287         //
1288         // Maybe there's a #include "you're holding it wrong" here.
1289         // However, I really don't want to hear from people who think
1290         // that a window can never be associated with something other
1291         // than a user file at time T1 and with a user file at time T2,
1292         // given that, in Wireshark, a window can be associated with a
1293         // live capture at time T1 and then, after you've saved the live
1294         // capture to a user file, associated with a user file at time T2.
1295         //
1296         if (cap_file_->is_tempfile) {
1297             //
1298             // For a temporary file, put the source of the data
1299             // in the window title, not whatever random pile
1300             // of characters is the last component of the path
1301             // name.
1302             //
1303             // XXX - on non-Mac platforms, put in the application
1304             // name?
1305             //
1306             gchar *window_name;
1307             setWindowFilePath(NULL);
1308             window_name = g_strdup_printf("Capturing from %s[*]", cf_get_tempfile_source(cap_file_)); //TODO : Fix Translate
1309             setWindowTitle(window_name);
1310             g_free(window_name);
1311         } else {
1312             //
1313             // For a user file, set the full path; that way,
1314             // for OS X, it'll set the "proxy icon".  Qt
1315             // handles extracting the last component.
1316             //
1317             // Sadly, some UN*Xes don't necessarily use UTF-8
1318             // for their file names, so we have to map the
1319             // file path to UTF-8.  If that fails, we're somewhat
1320             // stuck.
1321             //
1322             char *utf8_filename = g_filename_to_utf8(cap_file_->filename,
1323                                                      -1,
1324                                                      NULL,
1325                                                      NULL,
1326                                                      NULL);
1327             if (utf8_filename == NULL) {
1328                 // So what the heck else can we do here?
1329                 setWindowTitle(tr("(File name can't be mapped to UTF-8)"));
1330             } else {
1331                 setWindowTitle(NULL);
1332                 setWindowFilePath(NULL);
1333                 setWindowFilePath(utf8_filename);
1334                 g_free(utf8_filename);
1335             }
1336         }
1337         setWindowModified(cf_has_unsaved_data(cap_file_));
1338     } else {
1339         /* We have no capture file. */
1340         setWindowFilePath(NULL);
1341         setWindowTitle(tr("The Wireshark Network Analyzer"));
1342     }
1343 }
1344
1345 void MainWindow::setTitlebarForSelectedTreeRow()
1346 {
1347     setWindowTitle(tr("The Wireshark Network Analyzer"));
1348 }
1349
1350
1351 void MainWindow::setTitlebarForCaptureInProgress()
1352 {
1353     gchar *window_name;
1354
1355     setWindowFilePath(NULL);
1356     if (cap_file_) {
1357         window_name = g_strdup_printf("Capturing from %s", cf_get_tempfile_source(cap_file_)); //TODO : Fix Translate
1358         setWindowTitle(window_name);
1359         g_free(window_name);
1360     } else {
1361         /* We have no capture in progress. */
1362         setWindowTitle(tr("The Wireshark Network Analyzer"));
1363     }
1364 }
1365
1366 // Menu state
1367
1368 void MainWindow::setMenusForFollowStream()
1369 {
1370     if (!cap_file_)
1371         return;
1372
1373     if (!cap_file_->edt)
1374         return;
1375
1376     main_ui_->actionAnalyzeFollowTCPStream->setEnabled(false);
1377     main_ui_->actionAnalyzeFollowUDPStream->setEnabled(false);
1378     main_ui_->actionAnalyzeFollowSSLStream->setEnabled(false);
1379
1380     if (cap_file_->edt->pi.ipproto == IP_PROTO_TCP)
1381     {
1382         main_ui_->actionAnalyzeFollowTCPStream->setEnabled(true);
1383     }
1384
1385     if (cap_file_->edt->pi.ipproto == IP_PROTO_UDP)
1386     {
1387         main_ui_->actionAnalyzeFollowUDPStream->setEnabled(true);
1388     }
1389
1390     if ( epan_dissect_packet_contains_field(cap_file_->edt, "ssl") )
1391     {
1392         main_ui_->actionAnalyzeFollowSSLStream->setEnabled(true);
1393     }
1394 }
1395
1396 /* Enable or disable menu items based on whether you have a capture file
1397    you've finished reading and, if you have one, whether it's been saved
1398    and whether it could be saved except by copying the raw packet data. */
1399 void MainWindow::setMenusForCaptureFile(bool force_disable)
1400 {
1401     if (force_disable || cap_file_ == NULL || cap_file_->state == FILE_READ_IN_PROGRESS) {
1402         /* We have no capture file or we're currently reading a file */
1403         main_ui_->actionFileMerge->setEnabled(false);
1404         main_ui_->actionFileClose->setEnabled(false);
1405         main_ui_->actionFileSave->setEnabled(false);
1406         main_ui_->actionFileSaveAs->setEnabled(false);
1407         main_ui_->actionSummary->setEnabled(false);
1408         main_ui_->actionFileExportPackets->setEnabled(false);
1409         main_ui_->menuFileExportPacketDissections->setEnabled(false);
1410         main_ui_->actionFileExportPacketBytes->setEnabled(false);
1411         main_ui_->actionFileExportSSLSessionKeys->setEnabled(false);
1412         main_ui_->menuFileExportObjects->setEnabled(false);
1413         main_ui_->actionViewReload->setEnabled(false);
1414     } else {
1415         main_ui_->actionFileMerge->setEnabled(cf_can_write_with_wiretap(cap_file_));
1416
1417         main_ui_->actionFileClose->setEnabled(true);
1418         main_ui_->actionFileSave->setEnabled(cf_can_save(cap_file_));
1419         main_ui_->actionFileSaveAs->setEnabled(cf_can_save_as(cap_file_));
1420         main_ui_->actionSummary->setEnabled(true);
1421         /*
1422          * "Export Specified Packets..." should be available only if
1423          * we can write the file out in at least one format.
1424          */
1425         main_ui_->actionFileExportPackets->setEnabled(cf_can_write_with_wiretap(cap_file_));
1426         main_ui_->menuFileExportPacketDissections->setEnabled(true);
1427         main_ui_->actionFileExportPacketBytes->setEnabled(true);
1428         main_ui_->actionFileExportSSLSessionKeys->setEnabled(true);
1429         main_ui_->menuFileExportObjects->setEnabled(true);
1430         main_ui_->actionViewReload->setEnabled(true);
1431     }
1432 }
1433
1434 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
1435     /* Either a capture was started or stopped; in either case, it's not
1436        in the process of stopping, so allow quitting. */
1437
1438     main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
1439     main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
1440     main_ui_->menuFileExportPacketDissections->setEnabled(capture_in_progress);
1441     main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
1442     main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
1443     main_ui_->menuFileExportObjects->setEnabled(capture_in_progress);
1444     main_ui_->menuFileSet->setEnabled(!capture_in_progress);
1445     main_ui_->actionFileQuit->setEnabled(true);
1446
1447     main_ui_->actionSummary->setEnabled(capture_in_progress);
1448
1449     qDebug() << "FIX: packet list heading menu sensitivity";
1450     //    set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
1451     //                         !capture_in_progress);
1452     //    set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
1453     //                         !capture_in_progress);
1454     //    set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
1455     //                         !capture_in_progress);
1456
1457 #ifdef HAVE_LIBPCAP
1458     main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
1459     main_ui_->actionStartCapture->setEnabled(!capture_in_progress);
1460     main_ui_->actionStartCapture->setChecked(capture_in_progress);
1461     main_ui_->actionStopCapture->setEnabled(capture_in_progress);
1462     main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
1463 #endif /* HAVE_LIBPCAP */
1464
1465 }
1466
1467 void MainWindow::setMenusForCaptureStopping() {
1468     main_ui_->actionFileQuit->setEnabled(false);
1469     main_ui_->actionSummary->setEnabled(false);
1470 #ifdef HAVE_LIBPCAP
1471     main_ui_->actionStartCapture->setChecked(false);
1472     main_ui_->actionStopCapture->setEnabled(false);
1473     main_ui_->actionCaptureRestart->setEnabled(false);
1474 #endif /* HAVE_LIBPCAP */
1475 }
1476
1477 void MainWindow::setForCapturedPackets(bool have_captured_packets)
1478 {
1479     main_ui_->actionFilePrint->setEnabled(have_captured_packets);
1480
1481 //    set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
1482 //                         have_captured_packets);
1483
1484     main_ui_->actionEditFindPacket->setEnabled(have_captured_packets);
1485     main_ui_->actionEditFindNext->setEnabled(have_captured_packets);
1486     main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets);
1487 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/ZoomIn",
1488 //                         have_captured_packets);
1489 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/ZoomOut",
1490 //                         have_captured_packets);
1491 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/NormalSize",
1492 //                         have_captured_packets);
1493
1494     main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
1495     main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
1496     main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
1497     main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
1498     main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
1499
1500 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/PreviousPacketInConversation",
1501 //                         have_captured_packets);
1502 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/NextPacketInConversation",
1503 //                         have_captured_packets);
1504 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/StatisticsMenu/Summary",
1505 //                         have_captured_packets);
1506 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/StatisticsMenu/ProtocolHierarchy",
1507 //                         have_captured_packets);
1508 }
1509
1510 void MainWindow::setMenusForFileSet(bool enable_list_files) {
1511     bool enable_next = fileset_get_next() != NULL && enable_list_files;
1512     bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
1513
1514     main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
1515     main_ui_->actionFileSetNextFile->setEnabled(enable_next);
1516     main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
1517 }
1518
1519 void MainWindow::updateForUnsavedChanges() {
1520     setTitlebarForCaptureFile();
1521     setMenusForCaptureFile();
1522 //    set_toolbar_for_capture_file(cf);
1523
1524 }
1525
1526 /* Update main window items based on whether there's a capture in progress. */
1527 void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
1528 {
1529     setMenusForCaptureInProgress(capture_in_progress);
1530
1531 //#ifdef HAVE_LIBPCAP
1532 //    set_toolbar_for_capture_in_progress(capture_in_progress);
1533
1534 //    set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
1535 //#endif
1536 }
1537
1538 /*
1539  * Editor modelines
1540  *
1541  * Local Variables:
1542  * c-basic-offset: 4
1543  * tab-width: 8
1544  * indent-tabs-mode: nil
1545  * End:
1546  *
1547  * ex: set shiftwidth=4 tabstop=8 expandtab:
1548  * :indentSize=4:tabSize=8:noTabs=true:
1549  */