5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
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.
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.
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.
24 #include "main_window.h"
25 #include "ui_main_window.h"
29 #include <epan/filesystem.h>
30 #include <epan/prefs.h>
32 //#include <wiretap/wtap.h>
36 #include "capture-pcap-util.h"
37 #include "capture_ui_utils.h"
40 #include "ui/alert_box.h"
41 #include "ui/main_statusbar.h"
42 #include "ui/capture_globals.h"
44 #include "wireshark_application.h"
45 #include "proto_tree.h"
46 #include "byte_view_tab.h"
47 #include "display_filter_edit.h"
48 #include "import_text_dialog.h"
49 #include "export_dissection_dialog.h"
51 #include "qt_ui_utils.h"
53 #include <QTreeWidget>
56 #include <QToolButton>
58 #include <QMetaObject>
59 #include <QMessageBox>
61 //menu_recent_file_write_all
63 // If we ever add support for multiple windows this will need to be replaced.
64 static MainWindow *gbl_cur_main_window = NULL;
66 void pipe_input_set_handler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
68 gbl_cur_main_window->setPipeInputHandler(source, user_data, child_process, input_cb);
71 MainWindow::MainWindow(QWidget *parent) :
73 main_ui_(new Ui::MainWindow),
74 df_combo_box_(new DisplayFilterCombo()),
76 previous_focus_(NULL),
77 capture_stopping_(false),
84 QMargins go_to_margins;
86 gbl_cur_main_window = this;
87 main_ui_->setupUi(this);
88 setMenusForCaptureFile();
89 setForCapturedPackets(false);
90 setMenusForSelectedTreeRow();
91 setForCaptureInProgress(false);
92 setMenusForFileSet(false);
93 interfaceSelectionChanged();
95 connect(wsApp, SIGNAL(updateRecentItemStatus(const QString &, qint64, bool)), this, SLOT(updateRecentFiles()));
98 const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit());
99 connect(df_edit, SIGNAL(pushFilterSyntaxStatus(QString&)), main_ui_->statusBar, SLOT(pushFilterStatus(QString&)));
100 connect(df_edit, SIGNAL(popFilterSyntaxStatus()), main_ui_->statusBar, SLOT(popFilterStatus()));
101 connect(df_edit, SIGNAL(pushFilterSyntaxWarning(QString&)), main_ui_->statusBar, SLOT(pushTemporaryStatus(QString&)));
103 // http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
104 // http://qt-project.org/doc/qt-4.8/qstyle.html#StandardPixmap-enum
105 main_ui_->actionFileOpen->setIcon(
106 QIcon().fromTheme("document-open", style()->standardIcon(QStyle::SP_DirIcon)));
107 main_ui_->actionFileSave->setIcon(
108 QIcon().fromTheme("document-save", style()->standardIcon(QStyle::SP_FileIcon)));
109 main_ui_->actionFileClose->setIcon(
110 QIcon().fromTheme("process-stop", style()->standardIcon(QStyle::SP_BrowserStop)));
112 // In Qt4 multiple toolbars and "pretty" are mutually exculsive on OS X. If
113 // unifiedTitleAndToolBarOnMac is enabled everything ends up in the same row.
114 // https://bugreports.qt-project.org/browse/QTBUG-22433
115 // This property is obsolete in Qt5 so this issue may be fixed in that version.
116 main_ui_->displayFilterToolBar->addWidget(df_combo_box_);
118 main_ui_->goToFrame->hide();
119 go_to_margins = main_ui_->goToHB->contentsMargins();
120 // go_to_margins.setTop(0);
121 // go_to_margins.setBottom(0);
122 main_ui_->goToHB->setContentsMargins(go_to_margins);
123 // XXX For some reason the cursor is drawn funny with an input mask set
124 // https://bugreports.qt-project.org/browse/QTBUG-7174
125 main_ui_->goToFrame->setStyleSheet(
127 " background: palette(window);"
128 " padding-top: 0.1em;"
129 " padding-bottom: 0.1em;"
130 " border-bottom: 1px solid palette(shadow);"
137 #if defined(Q_WS_MAC)
138 foreach (QMenu *menu, main_ui_->menuBar->findChildren<QMenu*>()) {
139 foreach (QAction *act, menu->actions()) {
140 qDebug() << "disabling" << act->objectName();
141 act->setIconVisibleInMenu(false);
144 main_ui_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true);
145 main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true);
146 main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true);
149 packet_splitter_ = new QSplitter(main_ui_->mainStack);
150 packet_splitter_->setObjectName(QString::fromUtf8("splitterV"));
151 packet_splitter_->setOrientation(Qt::Vertical);
153 packet_list_ = new PacketList(packet_splitter_);
155 ProtoTree *proto_tree = new ProtoTree(packet_splitter_);
156 proto_tree->setHeaderHidden(true);
157 proto_tree->installEventFilter(this);
159 ByteViewTab *byte_view_tab = new ByteViewTab(packet_splitter_);
160 byte_view_tab->setTabPosition(QTabWidget::South);
161 byte_view_tab->setDocumentMode(true);
163 packet_list_->setProtoTree(proto_tree);
164 packet_list_->setByteViewTab(byte_view_tab);
165 packet_list_->installEventFilter(this);
167 packet_splitter_->addWidget(packet_list_);
168 packet_splitter_->addWidget(proto_tree);
169 packet_splitter_->addWidget(byte_view_tab);
171 main_ui_->mainStack->addWidget(packet_splitter_);
173 main_welcome_ = main_ui_->welcomePage;
176 connect(wsApp, SIGNAL(captureCapturePrepared(capture_options *)),
177 this, SLOT(captureCapturePrepared(capture_options *)));
178 connect(wsApp, SIGNAL(captureCaptureUpdateStarted(capture_options *)),
179 this, SLOT(captureCaptureUpdateStarted(capture_options *)));
180 connect(wsApp, SIGNAL(captureCaptureUpdateFinished(capture_options *)),
181 this, SLOT(captureCaptureUpdateFinished(capture_options *)));
182 connect(wsApp, SIGNAL(captureCaptureFixedStarted(capture_options *)),
183 this, SLOT(captureCaptureFixedStarted(capture_options *)));
184 connect(wsApp, SIGNAL(captureCaptureFixedFinished(capture_options *)),
185 this, SLOT(captureCaptureFixedFinished(capture_options *)));
186 connect(wsApp, SIGNAL(captureCaptureStopping(capture_options *)),
187 this, SLOT(captureCaptureStopping(capture_options *)));
188 connect(wsApp, SIGNAL(captureCaptureFailed(capture_options *)),
189 this, SLOT(captureCaptureFailed(capture_options *)));
192 connect(wsApp, SIGNAL(captureFileOpened(const capture_file*)),
193 this, SLOT(captureFileOpened(const capture_file*)));
194 connect(wsApp, SIGNAL(captureFileReadStarted(const capture_file*)),
195 this, SLOT(captureFileReadStarted(const capture_file*)));
196 connect(wsApp, SIGNAL(captureFileReadFinished(const capture_file*)),
197 this, SLOT(captureFileReadFinished(const capture_file*)));
198 connect(wsApp, SIGNAL(captureFileClosing(const capture_file*)),
199 this, SLOT(captureFileClosing(const capture_file*)));
200 connect(wsApp, SIGNAL(captureFileClosed(const capture_file*)),
201 this, SLOT(captureFileClosed(const capture_file*)));
203 connect(main_welcome_, SIGNAL(startCapture()),
204 this, SLOT(startCapture()));
205 connect(main_welcome_, SIGNAL(recentFileActivated(QString&)),
206 this, SLOT(openCaptureFile(QString&)));
208 connect(this, SIGNAL(setCaptureFile(capture_file*)),
209 main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
210 connect(this, SIGNAL(setCaptureFile(capture_file*)),
211 packet_list_, SLOT(setCaptureFile(capture_file*)));
212 connect(this, SIGNAL(setCaptureFile(capture_file*)),
213 byte_view_tab, SLOT(setCaptureFile(capture_file*)));
215 connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
216 packet_list_, SLOT(goNextPacket()));
217 connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
218 packet_list_, SLOT(goPreviousPacket()));
219 connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
220 packet_list_, SLOT(goFirstPacket()));
221 connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
222 packet_list_, SLOT(goLastPacket()));
224 connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
225 proto_tree, SLOT(expandSubtrees()));
226 connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
227 proto_tree, SLOT(expandAll()));
228 connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
229 proto_tree, SLOT(collapseAll()));
231 connect(proto_tree, SIGNAL(protoItemSelected(QString&)),
232 main_ui_->statusBar, SLOT(pushFieldStatus(QString&)));
234 connect(proto_tree, SIGNAL(protoItemSelected(field_info *)),
235 this, SLOT(setMenusForSelectedTreeRow(field_info *)));
237 connect(&file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString&)),
238 this, SLOT(openCaptureFile(QString&)));
240 QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
242 connect(iface_tree, SIGNAL(itemSelectionChanged()),
243 this, SLOT(interfaceSelectionChanged()));
245 main_ui_->mainStack->setCurrentWidget(main_welcome_);
248 MainWindow::~MainWindow()
254 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
256 pipe_source_ = source;
257 pipe_child_process_ = child_process;
258 pipe_user_data_ = user_data;
259 pipe_input_cb_ = input_cb;
262 /* Tricky to use pipes in win9x, as no concept of wait. NT can
263 do this but that doesn't cover all win32 platforms. GTK can do
264 this but doesn't seem to work over processes. Attempt to do
265 something similar here, start a timer and check for data on every
267 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
270 disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
274 pipe_timer_ = new QTimer(this);
275 connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
276 connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
277 pipe_timer_->start(200);
279 if (pipe_notifier_) {
280 disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
281 delete pipe_notifier_;
284 pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
285 // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
286 connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
287 connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
292 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
294 // The user typed some text. Start filling in a filter.
295 // We may need to be more choosy here. We just need to catch events for the packet list,
296 // proto tree, and main welcome widgets.
297 if (event->type() == QEvent::KeyPress) {
298 QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
299 if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
300 df_combo_box_->lineEdit()->insert(kevt->text());
301 df_combo_box_->lineEdit()->setFocus();
306 return QObject::eventFilter(obj, event);
309 void MainWindow::keyPressEvent(QKeyEvent *event) {
311 // Explicitly focus on the display filter combo.
312 if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
313 df_combo_box_->setFocus(Qt::ShortcutFocusReason);
317 if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
318 if (event->modifiers() == Qt::NoModifier) {
319 if (event->key() == Qt::Key_Escape) {
320 on_goToCancel_clicked();
321 } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
325 return; // goToLineEdit didn't want it and we don't either.
328 // Move up & down the packet list.
329 if (event->key() == Qt::Key_F7) {
330 packet_list_->goPreviousPacket();
331 } else if (event->key() == Qt::Key_F8) {
332 packet_list_->goNextPacket();
335 // Move along, citizen.
336 QMainWindow::keyPressEvent(event);
339 void MainWindow::closeEvent(QCloseEvent *event) {
340 /* If we're in the middle of stopping a capture, don't do anything;
341 the user can try deleting the window after the capture stops. */
342 if (capture_stopping_) {
348 void MainWindow::mergeCaptureFile()
350 QString file_name = "";
351 QString display_filter = "";
352 dfilter_t *rfcode = NULL;
358 if (prefs.gui_ask_unsaved) {
359 if (cap_file_->is_tempfile || cap_file_->unsaved_changes) {
360 QMessageBox msg_dialog;
361 gchar *display_basename;
364 msg_dialog.setIcon(QMessageBox::Question);
365 /* This is a temporary capture file or has unsaved changes; ask the
366 user whether to save the capture. */
367 if (cap_file_->is_tempfile) {
368 msg_dialog.setText(tr("Save packets before merging?"));
369 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
372 * Format the message.
374 display_basename = g_filename_display_basename(cap_file_->filename);
375 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
376 g_free(display_basename);
377 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
380 msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
381 msg_dialog.setDefaultButton(QMessageBox::Save);
383 response = msg_dialog.exec();
387 case QMessageBox::Save:
388 /* Save the file but don't close it */
389 saveCaptureFile(cap_file_, FALSE);
392 case QMessageBox::Cancel:
394 /* Don't do the merge. */
401 CaptureFileDialog merge_dlg(this, cap_file_, display_filter);
403 cf_status_t merge_status;
404 char *in_filenames[2];
407 switch (prefs.gui_fileopen_style) {
409 case FO_STYLE_LAST_OPENED:
410 /* The user has specified that we should start out in the last directory
411 we looked in. If we've already opened a file, use its containing
412 directory, if we could determine it, as the directory, otherwise
413 use the "last opened" directory saved in the preferences file if
415 /* This is now the default behaviour in file_selection_new() */
418 case FO_STYLE_SPECIFIED:
419 /* The user has specified that we should always start out in a
420 specified directory; if they've specified that directory,
421 start out by showing the files in that dir. */
422 if (prefs.gui_fileopen_dir[0] != '\0')
423 merge_dlg.setDirectory(prefs.gui_fileopen_dir);
427 if (merge_dlg.merge(file_name)) {
428 if (dfilter_compile(display_filter.toUtf8().constData(), &rfcode)) {
429 cf_set_rfcode(cap_file_, rfcode);
431 /* Not valid. Tell the user, and go back and run the file
432 selection box again once they dismiss the alert. */
433 //bad_dfilter_alert_box(top_level, display_filter->str);
434 QMessageBox::warning(this, tr("Invalid Display Filter"),
435 QString(tr("The filter expression %1 isn't a valid display filter. (%2).").arg(display_filter, dfilter_error_msg)),
443 file_type = cap_file_->cd_t;
445 /* Try to merge or append the two files */
447 if (merge_dlg.mergeType() == 0) {
448 /* chronological order */
449 in_filenames[0] = cap_file_->filename;
450 in_filenames[1] = file_name.toUtf8().data();
451 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
452 } else if (merge_dlg.mergeType() <= 0) {
454 in_filenames[0] = file_name.toUtf8().data();
455 in_filenames[1] = cap_file_->filename;
456 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
459 in_filenames[0] = cap_file_->filename;
460 in_filenames[1] = file_name.toUtf8().data();
461 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
464 if (merge_status != CF_OK) {
466 dfilter_free(rfcode);
473 /* Try to open the merged capture file. */
475 if (cf_open(&cfile, tmpname, TRUE /* temporary file */, &err) != CF_OK) {
476 /* We couldn't open it; fail. */
479 dfilter_free(rfcode);
484 /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
485 it closed the previous capture file, and thus destroyed any
486 previous read filter attached to "cf"). */
487 cfile.rfcode = rfcode;
489 switch (cf_read(&cfile, FALSE)) {
493 /* Just because we got an error, that doesn't mean we were unable
494 to read any of the file; we handle what we could get from the
498 case CF_READ_ABORTED:
499 /* The user bailed out of re-reading the capture file; the
500 capture file has been closed - just free the capture file name
501 string and return (without changing the last containing
507 /* Save the name of the containing directory specified in the path name,
508 if any; we can write over cf_merged_name, which is a good thing, given that
509 "get_dirname()" does write over its argument. */
510 wsApp->setLastOpenDir(get_dirname(tmpname));
512 df_combo_box_->setEditText(display_filter);
513 main_ui_->statusBar->showExpert();
519 void MainWindow::importCaptureFile() {
520 ImportTextDialog import_dlg;
522 if (!testCaptureFileClose(FALSE, *new QString(tr(" before importing a new capture"))))
527 if (import_dlg.result() != QDialog::Accepted) {
528 main_ui_->mainStack->setCurrentWidget(main_welcome_);
532 openCaptureFile(import_dlg.capfileName());
535 void MainWindow::saveCaptureFile(capture_file *cf, bool stay_closed) {
537 gboolean discard_comments;
539 if (cf->is_tempfile) {
540 /* This is a temporary capture file, so saving it means saving
541 it to a permanent file. Prompt the user for a location
542 to which to save it. Don't require that the file format
543 support comments - if it's a temporary capture file, it's
544 probably pcap-ng, which supports comments and, if it's
545 not pcap-ng, let the user decide what they want to do
546 if they've added comments. */
547 saveAsCaptureFile(cf, FALSE, stay_closed);
549 if (cf->unsaved_changes) {
550 cf_write_status_t status;
552 /* This is not a temporary capture file, but it has unsaved
553 changes, so saving it means doing a "safe save" on top
554 of the existing file, in the same format - no UI needed
555 unless the file has comments and the file's format doesn't
558 If the file has comments, does the file's format support them?
559 If not, ask the user whether they want to discard the comments
560 or choose a different format. */
561 switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
564 /* The file can be saved in the specified format as is;
565 just drive on and save in the format they selected. */
566 discard_comments = FALSE;
569 case SAVE_WITHOUT_COMMENTS:
570 /* The file can't be saved in the specified format as is,
571 but it can be saved without the comments, and the user
572 said "OK, discard the comments", so save it in the
573 format they specified without the comments. */
574 discard_comments = TRUE;
577 case SAVE_IN_ANOTHER_FORMAT:
578 /* There are file formats in which we can save this that
579 support comments, and the user said not to delete the
580 comments. Do a "Save As" so the user can select
581 one of those formats and choose a file name. */
582 saveAsCaptureFile(cf, TRUE, stay_closed);
586 /* The user said "forget it". Just return. */
590 /* Squelch warnings that discard_comments is being used
592 g_assert_not_reached();
596 /* XXX - cf->filename might get freed out from under us, because
597 the code path through which cf_save_packets() goes currently
598 closes the current file and then opens and reloads the saved file,
599 so make a copy and free it later. */
600 file_name = cf->filename;
601 status = cf_save_packets(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
602 discard_comments, stay_closed);
606 /* The save succeeded; we're done.
607 If we discarded comments, redraw the packet list to reflect
608 any packets that no longer have comments. */
609 if (discard_comments)
610 packet_list_queue_draw();
615 XXX - OK, what do we do now? Let them try a
616 "Save As", in case they want to try to save to a
617 different directory r file system? */
620 case CF_WRITE_ABORTED:
621 /* The write was aborted; just drive on. */
625 /* Otherwise just do nothing. */
629 void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool stay_closed) {
630 QString file_name = "";
633 cf_write_status_t status;
635 gboolean discard_comments = FALSE;
642 CaptureFileDialog save_as_dlg(this, cf);
644 switch (prefs.gui_fileopen_style) {
646 case FO_STYLE_LAST_OPENED:
647 /* The user has specified that we should start out in the last directory
648 we looked in. If we've already opened a file, use its containing
649 directory, if we could determine it, as the directory, otherwise
650 use the "last opened" directory saved in the preferences file if
652 /* This is now the default behaviour in file_selection_new() */
655 case FO_STYLE_SPECIFIED:
656 /* The user has specified that we should always start out in a
657 specified directory; if they've specified that directory,
658 start out by showing the files in that dir. */
659 if (prefs.gui_fileopen_dir[0] != '\0')
660 save_as_dlg.setDirectory(prefs.gui_fileopen_dir);
664 /* If the file has comments, does the format the user selected
665 support them? If not, ask the user whether they want to
666 discard the comments or choose a different format. */
667 switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
670 /* The file can be saved in the specified format as is;
671 just drive on and save in the format they selected. */
672 discard_comments = FALSE;
675 case SAVE_WITHOUT_COMMENTS:
676 /* The file can't be saved in the specified format as is,
677 but it can be saved without the comments, and the user
678 said "OK, discard the comments", so save it in the
679 format they specified without the comments. */
680 discard_comments = TRUE;
683 case SAVE_IN_ANOTHER_FORMAT:
684 /* There are file formats in which we can save this that
685 support comments, and the user said not to delete the
686 comments. The combo box of file formats has had the
687 formats that don't support comments trimmed from it,
688 so run the dialog again, to let the user decide
689 whether to save in one of those formats or give up. */
690 discard_comments = FALSE;
691 must_support_comments = TRUE;
695 /* The user said "forget it". Just get rid of the dialog box
699 file_type = save_as_dlg.selectedFileType();
700 compressed = save_as_dlg.isCompressed();
702 fileAddExtension(file_name, file_type, compressed);
705 // /* If the file exists and it's user-immutable or not writable,
706 // ask the user whether they want to override that. */
707 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
708 // /* They don't. Let them try another file name or cancel. */
713 /* Attempt to save the file */
714 status = cf_save_packets(cf, file_name.toUtf8().constData(), file_type, compressed,
715 discard_comments, stay_closed);
719 /* The save succeeded; we're done. */
720 /* Save the directory name for future file dialogs. */
721 dirname = get_dirname(file_name.toUtf8().data()); /* Overwrites cf_name */
722 set_last_open_dir(dirname);
723 /* If we discarded comments, redraw the packet list to reflect
724 any packets that no longer have comments. */
725 if (discard_comments)
726 packet_list_queue_draw();
730 /* The save failed; let the user try again. */
733 case CF_WRITE_ABORTED:
734 /* The user aborted the save; just return. */
741 void MainWindow::exportSelectedPackets() {
742 QString file_name = "";
745 packet_range_t range;
746 cf_write_status_t status;
748 gboolean discard_comments = FALSE;
753 /* Init the packet range */
754 packet_range_init(&range, cap_file_);
755 range.process_filtered = TRUE;
756 range.include_dependents = TRUE;
759 CaptureFileDialog esp_dlg(this, cap_file_);
761 switch (prefs.gui_fileopen_style) {
763 case FO_STYLE_LAST_OPENED:
764 /* The user has specified that we should start out in the last directory
765 we looked in. If we've already opened a file, use its containing
766 directory, if we could determine it, as the directory, otherwise
767 use the "last opened" directory saved in the preferences file if
769 /* This is now the default behaviour in file_selection_new() */
772 case FO_STYLE_SPECIFIED:
773 /* The user has specified that we should always start out in a
774 specified directory; if they've specified that directory,
775 start out by showing the files in that dir. */
776 if (prefs.gui_fileopen_dir[0] != '\0')
777 esp_dlg.setDirectory(prefs.gui_fileopen_dir);
781 /* If the file has comments, does the format the user selected
782 support them? If not, ask the user whether they want to
783 discard the comments or choose a different format. */
784 switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
787 /* The file can be saved in the specified format as is;
788 just drive on and save in the format they selected. */
789 discard_comments = FALSE;
792 case SAVE_WITHOUT_COMMENTS:
793 /* The file can't be saved in the specified format as is,
794 but it can be saved without the comments, and the user
795 said "OK, discard the comments", so save it in the
796 format they specified without the comments. */
797 discard_comments = TRUE;
800 case SAVE_IN_ANOTHER_FORMAT:
801 /* There are file formats in which we can save this that
802 support comments, and the user said not to delete the
803 comments. The combo box of file formats has had the
804 formats that don't support comments trimmed from it,
805 so run the dialog again, to let the user decide
806 whether to save in one of those formats or give up. */
807 discard_comments = FALSE;
811 /* The user said "forget it". Just get rid of the dialog box
817 * Check that we're not going to save on top of the current
819 * We do it here so we catch all cases ...
820 * Unfortunately, the file requester gives us an absolute file
821 * name and the read file name may be relative (if supplied on
822 * the command line). From Joerg Mayer.
824 if (files_identical(cap_file_->filename, file_name.toUtf8().constData())) {
826 gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
828 msg_box.setIcon(QMessageBox::Critical);
829 msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
830 msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
831 msg_box.setStandardButtons(QMessageBox::Ok);
832 msg_box.setDefaultButton(QMessageBox::Ok);
834 g_free(display_basename);
838 file_type = esp_dlg.selectedFileType();
839 compressed = esp_dlg.isCompressed();
840 fileAddExtension(file_name, file_type, compressed);
843 // /* If the file exists and it's user-immutable or not writable,
844 // ask the user whether they want to override that. */
845 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
846 // /* They don't. Let them try another file name or cancel. */
851 /* Attempt to save the file */
852 status = cf_export_specified_packets(cap_file_, file_name.toUtf8().constData(), &range, file_type, compressed);
856 /* The save succeeded; we're done. */
857 /* Save the directory name for future file dialogs. */
858 dirname = get_dirname(file_name.toUtf8().data()); /* Overwrites cf_name */
859 set_last_open_dir(dirname);
860 /* If we discarded comments, redraw the packet list to reflect
861 any packets that no longer have comments. */
862 if (discard_comments)
863 packet_list_queue_draw();
867 /* The save failed; let the user try again. */
870 case CF_WRITE_ABORTED:
871 /* The user aborted the save; just return. */
878 void MainWindow::exportDissections(export_type_e export_type) {
879 ExportDissectionDialog ed_dlg(this, cap_file_, export_type);
880 packet_range_t range;
885 /* Init the packet range */
886 packet_range_init(&range, cap_file_);
887 range.process_filtered = TRUE;
888 range.include_dependents = TRUE;
893 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
894 QString file_name_lower;
896 GSList *extensions_list;
897 gboolean add_extension;
900 * Append the default file extension if there's none given by
901 * the user or if they gave one that's not one of the valid
902 * extensions for the file type.
904 file_name_lower = file_name.toLower();
905 extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
906 if (extensions_list != NULL) {
909 /* We have one or more extensions for this file type.
910 Start out assuming we need to add the default one. */
911 add_extension = TRUE;
913 /* OK, see if the file has one of those extensions. */
914 for (extension = extensions_list; extension != NULL;
915 extension = g_slist_next(extension)) {
916 file_suffix += tr(".") + (char *)extension->data;
917 if (file_name_lower.endsWith(file_suffix)) {
919 * The file name has one of the extensions for
922 add_extension = FALSE;
925 file_suffix += ".gz";
926 if (file_name_lower.endsWith(file_suffix)) {
928 * The file name has one of the extensions for
931 add_extension = FALSE;
936 /* We have no extensions for this file type. Don't add one. */
937 add_extension = FALSE;
940 if (wtap_default_file_extension(file_type) != NULL) {
941 file_name += tr(".") + wtap_default_file_extension(file_type);
949 bool MainWindow::testCaptureFileClose(bool from_quit, QString &before_what) {
950 bool capture_in_progress = FALSE;
952 if (!cap_file_ || cap_file_->state == FILE_CLOSED)
953 return true; /* Already closed, nothing to do */
956 if (cap_file_->state == FILE_READ_IN_PROGRESS) {
957 /* This is true if we're reading a capture file *or* if we're doing
958 a live capture. If we're reading a capture file, the main loop
959 is busy reading packets, and only accepting input from the
960 progress dialog, so we can't get here, so this means we're
962 capture_in_progress = TRUE;
966 if (prefs.gui_ask_unsaved) {
967 if (cap_file_->is_tempfile || capture_in_progress || cap_file_->unsaved_changes) {
968 QMessageBox msg_dialog;
970 QPushButton *default_button;
973 msg_dialog.setIcon(QMessageBox::Question);
975 /* This is a temporary capture file, or there's a capture in
976 progress, or the file has unsaved changes; ask the user whether
978 if (cap_file_->is_tempfile) {
980 msg_dialog.setText(tr("You have unsaved packets"));
981 msg_dialog.setInformativeText(tr("They will be lost if you don't save them."));
983 if (capture_in_progress) {
984 question.append(tr("Do you want to stop the capture and save the captured packets"));
986 question.append(tr("Do you want to save the captured packets"));
988 question.append(before_what).append(tr("?"));
989 msg_dialog.setInformativeText(question);
994 * Format the message.
996 if (capture_in_progress) {
997 question.append(tr("Do you want to stop the capture and save the captured packets"));
998 question.append(before_what).append(tr("?"));
999 msg_dialog.setInformativeText(tr("Your captured packets will be lost if you don't save them."));
1001 gchar *display_basename = g_filename_display_basename(cap_file_->filename);
1002 question.append(QString(tr("Do you want to save the changes you've made to the capture file \"%1\"%2?"))
1003 .arg(display_basename)
1006 g_free(display_basename);
1007 msg_dialog.setInformativeText(tr("Your changes will be lost if you don't save them."));
1011 // XXX Text comes from ui/gtk/stock_icons.[ch]
1012 // Note that the button roles differ from the GTK+ version.
1013 // Cancel = RejectRole
1014 // Save = AcceptRole
1015 // Don't Save = DestructiveRole
1016 msg_dialog.setStandardButtons(QMessageBox::Cancel);
1018 if (capture_in_progress) {
1019 default_button = msg_dialog.addButton(tr("Stop and Save"), QMessageBox::AcceptRole);
1021 default_button = msg_dialog.addButton(QMessageBox::Save);
1023 msg_dialog.setDefaultButton(default_button);
1026 if (cap_file_->state == FILE_READ_IN_PROGRESS) {
1027 msg_dialog.addButton(tr("Stop and Quit without Saving"), QMessageBox::DestructiveRole);
1029 msg_dialog.addButton(tr("Quit without Saving"), QMessageBox::DestructiveRole);
1032 if (capture_in_progress) {
1033 msg_dialog.addButton(tr("Stop and Continue without Saving"), QMessageBox::DestructiveRole);
1035 msg_dialog.addButton(QMessageBox::Discard);
1039 response = msg_dialog.exec();
1043 case QMessageBox::Save:
1045 /* If there's a capture in progress, we have to stop the capture
1046 and then do the save. */
1047 if (capture_in_progress)
1050 /* Save the file and close it */
1051 saveCaptureFile(cap_file_, TRUE);
1054 case QMessageBox::Discard:
1057 * If there's a capture in progress; we have to stop the capture
1058 * and then do the close.
1060 if (capture_in_progress)
1063 /* Just close the file, discarding changes */
1064 cf_close(cap_file_);
1068 case QMessageBox::Cancel:
1070 /* Don't close the file (and don't stop any capture in progress). */
1071 return false; /* file not closed */
1075 /* Unchanged file, just close it */
1076 cf_close(cap_file_);
1079 /* User asked not to be bothered by those prompts, just close it.
1080 XXX - should that apply only to saving temporary files? */
1082 /* If there's a capture in progress, we have to stop the capture
1083 and then do the close. */
1084 if (capture_in_progress)
1087 cf_close(cap_file_);
1090 return true; /* File closed */
1093 void MainWindow::captureStop() {
1096 while(cap_file_ && cap_file_->state == FILE_READ_IN_PROGRESS) {
1097 WiresharkApplication::processEvents();
1103 /* Enable or disable menu items based on whether you have a capture file
1104 you've finished reading and, if you have one, whether it's been saved
1105 and whether it could be saved except by copying the raw packet data. */
1106 void MainWindow::setMenusForCaptureFile(bool force_disable)
1108 if (force_disable || cap_file_ == NULL || cap_file_->state == FILE_READ_IN_PROGRESS) {
1109 /* We have no capture file or we're currently reading a file */
1110 main_ui_->actionFileMerge->setEnabled(false);
1111 main_ui_->actionFileClose->setEnabled(false);
1112 main_ui_->actionFileSave->setEnabled(false);
1113 main_ui_->actionFileSaveAs->setEnabled(false);
1114 main_ui_->actionFileExportPackets->setEnabled(false);
1115 main_ui_->menuFileExportPacketDissections->setEnabled(false);
1116 main_ui_->actionFileExportPacketBytes->setEnabled(false);
1117 main_ui_->actionFileExportSSLSessionKeys->setEnabled(false);
1118 main_ui_->menuFileExportObjects->setEnabled(false);
1119 main_ui_->actionViewReload->setEnabled(false);
1121 main_ui_->actionFileMerge->setEnabled(cf_can_write_with_wiretap(cap_file_));
1123 main_ui_->actionFileClose->setEnabled(true);
1125 * "Save" should be available only if:
1127 * the file has unsaved changes, and we can save it in some
1128 * format through Wiretap
1132 * the file is a temporary file and has no unsaved changes (so
1133 * that "saving" it just means copying it).
1135 main_ui_->actionFileSave->setEnabled(
1136 (cap_file_->unsaved_changes && cf_can_write_with_wiretap(cap_file_)) ||
1137 (cap_file_->is_tempfile && !cap_file_->unsaved_changes));
1139 * "Save As..." should be available only if:
1141 * we can save it in some format through Wiretap
1145 * the file is a temporary file and has no unsaved changes (so
1146 * that "saving" it just means copying it).
1148 main_ui_->actionFileSaveAs->setEnabled(
1149 cf_can_write_with_wiretap(cap_file_) ||
1150 (cap_file_->is_tempfile && !cap_file_->unsaved_changes));
1152 * "Export Specified Packets..." should be available only if
1153 * we can write the file out in at least one format.
1155 main_ui_->actionFileExportPackets->setEnabled(cf_can_write_with_wiretap(cap_file_));
1156 main_ui_->menuFileExportPacketDissections->setEnabled(true);
1157 main_ui_->actionFileExportPacketBytes->setEnabled(true);
1158 main_ui_->actionFileExportSSLSessionKeys->setEnabled(true);
1159 main_ui_->menuFileExportObjects->setEnabled(true);
1160 main_ui_->actionViewReload->setEnabled(true);
1164 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
1165 /* Either a capture was started or stopped; in either case, it's not
1166 in the process of stopping, so allow quitting. */
1168 main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
1169 main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
1170 main_ui_->menuFileExportPacketDissections->setEnabled(capture_in_progress);
1171 main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
1172 main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
1173 main_ui_->menuFileExportObjects->setEnabled(capture_in_progress);
1174 main_ui_->menuFileSet->setEnabled(!capture_in_progress);
1175 main_ui_->actionFileQuit->setEnabled(true);
1177 qDebug() << "FIX: packet list heading menu sensitivity";
1178 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
1179 // !capture_in_progress);
1180 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
1181 // !capture_in_progress);
1182 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
1183 // !capture_in_progress);
1186 main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
1187 main_ui_->actionStartCapture->setEnabled(!capture_in_progress);
1188 main_ui_->actionStartCapture->setChecked(capture_in_progress);
1189 main_ui_->actionStopCapture->setEnabled(capture_in_progress);
1190 main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
1191 #endif /* HAVE_LIBPCAP */
1195 void MainWindow::setMenusForCaptureStopping() {
1196 main_ui_->actionFileQuit->setEnabled(false);
1198 main_ui_->actionStartCapture->setChecked(false);
1199 main_ui_->actionStopCapture->setEnabled(false);
1200 main_ui_->actionCaptureRestart->setEnabled(false);
1201 #endif /* HAVE_LIBPCAP */
1204 void MainWindow::setForCapturedPackets(bool have_captured_packets)
1206 main_ui_->actionFilePrint->setEnabled(have_captured_packets);
1208 // set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
1209 // have_captured_packets);
1211 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/FindPacket",
1212 // have_captured_packets);
1213 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/FindNext",
1214 // have_captured_packets);
1215 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/FindPrevious",
1216 // have_captured_packets);
1217 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/ZoomIn",
1218 // have_captured_packets);
1219 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/ZoomOut",
1220 // have_captured_packets);
1221 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/NormalSize",
1222 // have_captured_packets);
1224 main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
1225 main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
1226 main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
1227 main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
1228 main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
1230 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/PreviousPacketInConversation",
1231 // have_captured_packets);
1232 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/NextPacketInConversation",
1233 // have_captured_packets);
1234 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/StatisticsMenu/Summary",
1235 // have_captured_packets);
1236 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/StatisticsMenu/ProtocolHierarchy",
1237 // have_captured_packets);
1240 void MainWindow::setMenusForFileSet(bool enable_list_files) {
1241 bool enable_next = fileset_get_next() != NULL && enable_list_files;
1242 bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
1244 main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
1245 main_ui_->actionFileSetNextFile->setEnabled(enable_next);
1246 main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
1249 void MainWindow::updateForUnsavedChanges() {
1250 // set_display_filename(cf);
1251 setMenusForCaptureFile();
1252 // set_toolbar_for_capture_file(cf);
1256 /* Update main window items based on whether there's a capture in progress. */
1257 void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
1259 setMenusForCaptureInProgress(capture_in_progress);
1261 //#ifdef HAVE_LIBPCAP
1262 // set_toolbar_for_capture_in_progress(capture_in_progress);
1264 // set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
1274 * indent-tabs-mode: nil
1277 * ex: set shiftwidth=4 tabstop=8 expandtab:
1278 * :indentSize=4:tabSize=8:noTabs=true: