3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "main_window.h"
23 #include "ui_main_window.h"
27 #include <epan/addr_resolv.h>
28 #include <epan/epan_dissect.h>
29 #include <wsutil/filesystem.h>
30 #include <epan/prefs.h>
32 //#include <wiretap/wtap.h>
35 #include "ui/capture.h"
36 #include <capchild/capture_session.h>
39 #include "ui/alert_box.h"
41 #include "ui/capture_ui_utils.h"
43 #include "ui/capture_globals.h"
44 #include "ui/main_statusbar.h"
45 #include "ui/recent.h"
48 #include "byte_view_tab.h"
49 #include "display_filter_edit.h"
50 #include "export_dissection_dialog.h"
51 #include "import_text_dialog.h"
52 #include "proto_tree.h"
53 #include "simple_dialog.h"
54 #include "stock_icon.h"
55 #include "wireshark_application.h"
57 #include "qt_ui_utils.h"
60 #include <QActionGroup>
61 #include <QDesktopWidget>
63 #include <QMessageBox>
64 #include <QMetaObject>
65 #include <QPropertyAnimation>
67 #include <QToolButton>
68 #include <QTreeWidget>
70 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
71 #include <QtMacExtras/QMacNativeToolBar>
76 //menu_recent_file_write_all
78 // If we ever add support for multiple windows this will need to be replaced.
79 static MainWindow *gbl_cur_main_window_ = NULL;
81 void pipe_input_set_handler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
83 gbl_cur_main_window_->setPipeInputHandler(source, user_data, child_process, input_cb);
87 simple_dialog(ESD_TYPE_E type, gint btn_mask, const gchar *msg_format, ...)
91 va_start(ap, msg_format);
92 SimpleDialog sd(gbl_cur_main_window_, type, btn_mask, msg_format, ap);
100 * Alert box, with optional "don't show this message again" variable
101 * and checkbox, and optional secondary text.
104 simple_message_box(ESD_TYPE_E type, gboolean *notagain,
105 const char *secondary_msg, const char *msg_format, ...)
107 if (notagain && *notagain) {
113 va_start(ap, msg_format);
114 SimpleDialog sd(gbl_cur_main_window_, type, ESD_BTN_OK, msg_format, ap);
117 sd.setDetailedText(secondary_msg);
119 #if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
120 QCheckBox *cb = new QCheckBox();
122 cb->setChecked(true);
123 cb->setText(QObject::tr("Don't show this message again."));
130 #if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
132 *notagain = cb->isChecked();
138 * Error alert box, taking a format and a va_list argument.
141 vsimple_error_message_box(const char *msg_format, va_list ap)
143 SimpleDialog sd(gbl_cur_main_window_, ESD_TYPE_ERROR, ESD_BTN_OK, msg_format, ap);
148 MainWindow::MainWindow(QWidget *parent) :
150 main_ui_(new Ui::MainWindow),
151 df_combo_box_(new DisplayFilterCombo()),
153 previous_focus_(NULL),
154 show_hide_actions_(NULL),
155 time_display_actions_(NULL),
156 time_precision_actions_(NULL),
157 capture_stopping_(false),
158 capture_filter_valid_(false),
165 if (!gbl_cur_main_window_) {
166 connect(wsApp, SIGNAL(openStatCommandDialog(QString,const char*,void*)),
167 this, SLOT(openStatCommandDialog(QString,const char*,void*)));
169 gbl_cur_main_window_ = this;
171 capture_session_init(&cap_session_, (void *)&cfile);
173 main_ui_->setupUi(this);
174 setTitlebarForCaptureFile();
175 setMenusForCaptureFile();
176 setForCapturedPackets(false);
177 setMenusForSelectedPacket();
178 setMenusForSelectedTreeRow();
179 setForCaptureInProgress(false);
180 setMenusForFileSet(false);
181 interfaceSelectionChanged();
183 //To prevent users use features before initialization complete
184 //Otherwise unexpected problems may occur
185 setFeaturesEnabled(false);
186 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(setFeaturesEnabled()));
187 connect(wsApp, SIGNAL(appInitialized()), this, SLOT(zoomText()));
189 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutPanes()));
190 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutToolbars()));
191 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(updateNameResolutionActions()));
192 connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(zoomText()));
194 connect(wsApp, SIGNAL(recentFilesRead()), this, SLOT(loadWindowGeometry()));
196 connect(wsApp, SIGNAL(updateRecentItemStatus(const QString &, qint64, bool)), this, SLOT(updateRecentFiles()));
200 connect(&capture_interfaces_dialog_, SIGNAL(startCapture()), this, SLOT(startCapture()));
201 connect(&capture_interfaces_dialog_, SIGNAL(stopCapture()), this, SLOT(stopCapture()));
204 const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit());
205 connect(df_edit, SIGNAL(pushFilterSyntaxStatus(QString&)), main_ui_->statusBar, SLOT(pushFilterStatus(QString&)));
206 connect(df_edit, SIGNAL(popFilterSyntaxStatus()), main_ui_->statusBar, SLOT(popFilterStatus()));
207 connect(df_edit, SIGNAL(pushFilterSyntaxWarning(QString&)),
208 main_ui_->statusBar, SLOT(pushTemporaryStatus(QString&)));
209 connect(df_edit, SIGNAL(filterPackets(QString&,bool)), this, SLOT(filterPackets(QString&,bool)));
210 connect(df_edit, SIGNAL(addBookmark(QString)), this, SLOT(addDisplayFilterButton(QString)));
211 connect(this, SIGNAL(displayFilterSuccess(bool)), df_edit, SLOT(displayFilterSuccess(bool)));
213 initMainToolbarIcons();
215 // In Qt4 multiple toolbars and "pretty" are mutually exculsive on OS X. If
216 // unifiedTitleAndToolBarOnMac is enabled everything ends up in the same row.
217 // https://bugreports.qt-project.org/browse/QTBUG-22433
218 // This property is obsolete in Qt5 so this issue may be fixed in that version.
219 main_ui_->displayFilterToolBar->addWidget(df_combo_box_);
221 main_ui_->goToFrame->hide();
222 // XXX For some reason the cursor is drawn funny with an input mask set
223 // https://bugreports.qt-project.org/browse/QTBUG-7174
225 main_ui_->searchFrame->hide();
226 connect(main_ui_->searchFrame, SIGNAL(pushFilterSyntaxStatus(QString&)),
227 main_ui_->statusBar, SLOT(pushTemporaryStatus(QString&)));
230 main_ui_->menuCapture->setEnabled(false);
233 #if defined(Q_OS_MAC)
234 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
235 QMacNativeToolBar *ntb = QtMacExtras::setNativeToolBar(main_ui_->mainToolBar);
236 ntb->setIconSize(QSize(24, 24));
237 #endif // QT_MACEXTRAS_LIB
239 main_ui_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true);
240 main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true);
241 main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true);
243 main_ui_->actionEditPreferences->setMenuRole(QAction::PreferencesRole);
247 #ifdef HAVE_SOFTWARE_UPDATE
248 QAction *update_sep = main_ui_->menuHelp->insertSeparator(main_ui_->actionHelpAbout);
249 QAction *update_action = new QAction(tr("Check for Updates..."), main_ui_->menuHelp);
250 main_ui_->menuHelp->insertAction(update_sep, update_action);
251 connect(update_action, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
253 master_split_.setObjectName(tr("splitterMaster"));
254 extra_split_.setObjectName(tr("splitterExtra"));
255 main_ui_->mainStack->addWidget(&master_split_);
257 empty_pane_.setObjectName(tr("emptyPane"));
259 packet_list_ = new PacketList(&master_split_);
261 proto_tree_ = new ProtoTree(&master_split_);
262 proto_tree_->setHeaderHidden(true);
263 proto_tree_->installEventFilter(this);
265 byte_view_tab_ = new ByteViewTab(&master_split_);
266 byte_view_tab_->setTabPosition(QTabWidget::South);
267 byte_view_tab_->setDocumentMode(true);
269 packet_list_->setProtoTree(proto_tree_);
270 packet_list_->setByteViewTab(byte_view_tab_);
271 packet_list_->installEventFilter(this);
273 main_welcome_ = main_ui_->welcomePage;
275 initShowHideMainWidgets();
276 initTimeDisplayFormatMenu();
277 initTimePrecisionFormatMenu();
278 updateNameResolutionActions();
280 connect(wsApp, SIGNAL(captureCapturePrepared(capture_session *)),
281 this, SLOT(captureCapturePrepared(capture_session *)));
282 connect(wsApp, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
283 this, SLOT(captureCaptureUpdateStarted(capture_session *)));
284 connect(wsApp, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
285 this, SLOT(captureCaptureUpdateFinished(capture_session *)));
286 connect(wsApp, SIGNAL(captureCaptureFixedStarted(capture_session *)),
287 this, SLOT(captureCaptureFixedStarted(capture_session *)));
288 connect(wsApp, SIGNAL(captureCaptureFixedFinished(capture_session *)),
289 this, SLOT(captureCaptureFixedFinished(capture_session *)));
290 connect(wsApp, SIGNAL(captureCaptureStopping(capture_session *)),
291 this, SLOT(captureCaptureStopping(capture_session *)));
292 connect(wsApp, SIGNAL(captureCaptureFailed(capture_session *)),
293 this, SLOT(captureCaptureFailed(capture_session *)));
295 connect(wsApp, SIGNAL(captureFileOpened(const capture_file*)),
296 this, SLOT(captureFileOpened(const capture_file*)));
297 connect(wsApp, SIGNAL(captureFileReadStarted(const capture_file*)),
298 this, SLOT(captureFileReadStarted(const capture_file*)));
299 connect(wsApp, SIGNAL(captureFileReadFinished(const capture_file*)),
300 this, SLOT(captureFileReadFinished(const capture_file*)));
301 connect(wsApp, SIGNAL(captureFileClosing(const capture_file*)),
302 this, SLOT(captureFileClosing(const capture_file*)));
303 connect(wsApp, SIGNAL(captureFileClosed(const capture_file*)),
304 this, SLOT(captureFileClosed(const capture_file*)));
305 connect(wsApp, SIGNAL(columnsChanged()),
306 this, SLOT(recreatePacketList()));
307 connect(wsApp, SIGNAL(packetDissectionChanged()),
308 this, SLOT(redissectPackets()));
309 connect(wsApp, SIGNAL(appInitialized()),
310 this, SLOT(filterExpressionsChanged()));
311 connect(wsApp, SIGNAL(filterExpressionsChanged()),
312 this, SLOT(filterExpressionsChanged()));
313 connect(wsApp, SIGNAL(fieldsChanged()),
314 this, SLOT(fieldsChanged()));
316 connect(main_welcome_, SIGNAL(startCapture()),
317 this, SLOT(startCapture()));
318 connect(main_welcome_, SIGNAL(recentFileActivated(QString&)),
319 this, SLOT(openCaptureFile(QString&)));
320 connect(main_welcome_, SIGNAL(pushFilterSyntaxStatus(QString&)),
321 main_ui_->statusBar, SLOT(pushFilterStatus(QString&)));
322 connect(main_welcome_, SIGNAL(popFilterSyntaxStatus()),
323 main_ui_->statusBar, SLOT(popFilterStatus()));
325 connect(this, SIGNAL(setCaptureFile(capture_file*)),
326 main_ui_->searchFrame, SLOT(setCaptureFile(capture_file*)));
327 connect(this, SIGNAL(setCaptureFile(capture_file*)),
328 main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
329 connect(this, SIGNAL(setCaptureFile(capture_file*)),
330 packet_list_, SLOT(setCaptureFile(capture_file*)));
331 connect(this, SIGNAL(setCaptureFile(capture_file*)),
332 byte_view_tab_, SLOT(setCaptureFile(capture_file*)));
334 connect(this, SIGNAL(monospaceFontChanged(QFont)),
335 packet_list_, SLOT(setMonospaceFont(QFont)));
336 connect(this, SIGNAL(monospaceFontChanged(QFont)),
337 proto_tree_, SLOT(setMonospaceFont(QFont)));
338 connect(this, SIGNAL(monospaceFontChanged(QFont)),
339 byte_view_tab_, SLOT(setMonospaceFont(QFont)));
341 connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
342 packet_list_, SLOT(goNextPacket()));
343 connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
344 packet_list_, SLOT(goPreviousPacket()));
345 connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
346 packet_list_, SLOT(goFirstPacket()));
347 connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
348 packet_list_, SLOT(goLastPacket()));
350 connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
351 proto_tree_, SLOT(expandSubtrees()));
352 connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
353 proto_tree_, SLOT(expandAll()));
354 connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
355 proto_tree_, SLOT(collapseAll()));
357 connect(packet_list_, SIGNAL(packetSelectionChanged()),
358 this, SLOT(setMenusForSelectedPacket()));
359 connect(packet_list_, SIGNAL(packetDissectionChanged()),
360 this, SLOT(redissectPackets()));
361 connect(packet_list_, SIGNAL(packetSelectionChanged()),
362 this, SLOT(setMenusForFollowStream()));
364 connect(proto_tree_, SIGNAL(protoItemSelected(QString&)),
365 main_ui_->statusBar, SLOT(pushFieldStatus(QString&)));
366 connect(proto_tree_, SIGNAL(protoItemSelected(field_info *)),
367 this, SLOT(setMenusForSelectedTreeRow(field_info *)));
369 connect(byte_view_tab_, SIGNAL(byteFieldHovered(QString&)),
370 main_ui_->statusBar, SLOT(pushByteStatus(QString&)));
372 connect(main_ui_->statusBar, SIGNAL(editCaptureComment()),
373 this, SLOT(on_actionStatisticsCaptureFileProperties_triggered()));
375 connect(&file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString&)),
376 this, SLOT(openCaptureFile(QString&)));
379 QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
381 connect(iface_tree, SIGNAL(itemSelectionChanged()),
382 this, SLOT(interfaceSelectionChanged()));
384 connect(main_ui_->welcomePage, SIGNAL(captureFilterSyntaxChanged(bool)),
385 this, SLOT(captureFilterSyntaxChanged(bool)));
387 connect(&capture_interfaces_dialog_, SIGNAL(getPoints(int,PointList*)),
388 this->main_welcome_->getInterfaceTree(), SLOT(getPoints(int,PointList*)));
389 connect(&capture_interfaces_dialog_, SIGNAL(setSelectedInterfaces()),
390 this->main_welcome_->getInterfaceTree(), SLOT(setSelectedInterfaces()));
391 connect(&capture_interfaces_dialog_, SIGNAL(interfaceListChanged()),
392 this->main_welcome_->getInterfaceTree(), SLOT(interfaceListChanged()));
395 main_ui_->mainStack->setCurrentWidget(main_welcome_);
398 MainWindow::~MainWindow()
403 QString MainWindow::getFilter()
405 return df_combo_box_->itemText(df_combo_box_->count());
408 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
410 pipe_source_ = source;
411 pipe_child_process_ = child_process;
412 pipe_user_data_ = user_data;
413 pipe_input_cb_ = input_cb;
416 /* Tricky to use pipes in win9x, as no concept of wait. NT can
417 do this but that doesn't cover all win32 platforms. GTK can do
418 this but doesn't seem to work over processes. Attempt to do
419 something similar here, start a timer and check for data on every
421 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
424 disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
428 pipe_timer_ = new QTimer(this);
429 connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
430 connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
431 pipe_timer_->start(200);
433 if (pipe_notifier_) {
434 disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
435 delete pipe_notifier_;
438 pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
439 // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
440 connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
441 connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
446 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
448 // The user typed some text. Start filling in a filter.
449 // We may need to be more choosy here. We just need to catch events for the packet list,
450 // proto tree, and main welcome widgets.
451 if (event->type() == QEvent::KeyPress) {
452 QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
453 if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
454 df_combo_box_->lineEdit()->insert(kevt->text());
455 df_combo_box_->lineEdit()->setFocus();
460 return QMainWindow::eventFilter(obj, event);
463 void MainWindow::keyPressEvent(QKeyEvent *event) {
465 // Explicitly focus on the display filter combo.
466 if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
467 df_combo_box_->setFocus(Qt::ShortcutFocusReason);
471 if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
472 if (event->modifiers() == Qt::NoModifier) {
473 if (event->key() == Qt::Key_Escape) {
474 on_goToCancel_clicked();
475 } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
479 return; // goToLineEdit didn't want it and we don't either.
482 // Move up & down the packet list.
483 if (event->key() == Qt::Key_F7) {
484 packet_list_->goPreviousPacket();
485 } else if (event->key() == Qt::Key_F8) {
486 packet_list_->goNextPacket();
489 // Move along, citizen.
490 QMainWindow::keyPressEvent(event);
493 void MainWindow::closeEvent(QCloseEvent *event) {
494 saveWindowGeometry();
496 /* If we're in the middle of stopping a capture, don't do anything;
497 the user can try deleting the window after the capture stops. */
498 if (capture_stopping_) {
504 capture_interfaces_dialog_.close();
506 // Make sure we kill any open dumpcap processes.
507 delete main_welcome_;
509 if(!wsApp->isInitialized()) {
510 // If we're still initializing, QCoreApplication::quit() won't
511 // exit properly because we are not in the event loop. This
512 // means that the application won't clean up after itself. We
513 // might want to call wsApp->processEvents() during startup
514 // instead so that we can do a normal exit here.
519 const int min_sensible_dimension = 200;
520 const int geom_animation_duration = 150;
521 void MainWindow::loadWindowGeometry()
523 QWidget shadow_main(wsApp->desktop());
524 shadow_main.setVisible(false);
526 // Start off with the Widget defaults
527 shadow_main.restoreGeometry(saveGeometry());
529 // Apply any saved settings
531 // Note that we're saving and restoring the outer window frame
532 // position and the inner client area size.
533 // if (prefs.gui_geometry_save_position) {
534 shadow_main.move(recent.gui_geometry_main_x, recent.gui_geometry_main_y);
537 // XXX Preferences haven't been loaded at this point. For now we
538 // assume default (true) values for everything.
540 if (// prefs.gui_geometry_save_size &&
541 recent.gui_geometry_main_width > min_sensible_dimension &&
542 recent.gui_geometry_main_height > min_sensible_dimension) {
543 shadow_main.resize(recent.gui_geometry_main_width, recent.gui_geometry_main_height);
546 // Let Qt move and resize our window if needed (e.g. if it's offscreen)
547 QByteArray geom = shadow_main.saveGeometry();
550 if (/* prefs.gui_geometry_save_maximized && */ recent.gui_geometry_main_maximized) {
551 setWindowState(Qt::WindowMaximized);
554 if (strlen (get_conn_cfilter()) < 1) {
555 QPropertyAnimation *pos_anim = new QPropertyAnimation(this, "pos");
556 QPropertyAnimation *size_anim = new QPropertyAnimation(this, "size");
558 shadow_main.restoreGeometry(geom);
560 pos_anim->setDuration(geom_animation_duration);
561 pos_anim->setStartValue(pos());
562 pos_anim->setEndValue(shadow_main.pos());
563 pos_anim->setEasingCurve(QEasingCurve::InOutQuad);
564 size_anim->setDuration(geom_animation_duration);
565 size_anim->setStartValue(size());
566 size_anim->setEasingCurve(QEasingCurve::InOutQuad);
567 size_anim->setEndValue(shadow_main.size());
569 pos_anim->start(QAbstractAnimation::DeleteWhenStopped);
570 size_anim->start(QAbstractAnimation::DeleteWhenStopped);
572 restoreGeometry(geom);
577 void MainWindow::saveWindowGeometry()
579 if (prefs.gui_geometry_save_position) {
580 recent.gui_geometry_main_x = pos().x();
581 recent.gui_geometry_main_y = pos().y();
584 if (prefs.gui_geometry_save_size) {
585 recent.gui_geometry_main_width = size().width();
586 recent.gui_geometry_main_height = size().height();
589 if (prefs.gui_geometry_save_maximized) {
590 // On OS X this is false when it shouldn't be
591 recent.gui_geometry_main_maximized = isMaximized();
595 QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) {
597 case layout_pane_content_none:
599 case layout_pane_content_plist:
601 case layout_pane_content_pdetails:
603 case layout_pane_content_pbytes:
604 return byte_view_tab_;
606 g_assert_not_reached();
611 void MainWindow::mergeCaptureFile()
613 QString file_name = "";
614 QString display_filter = "";
615 dfilter_t *rfcode = NULL;
621 if (prefs.gui_ask_unsaved) {
622 if (cf_has_unsaved_data(cap_file_)) {
623 QMessageBox msg_dialog;
624 gchar *display_basename;
627 msg_dialog.setIcon(QMessageBox::Question);
628 /* This file has unsaved data; ask the user whether to save
630 if (cap_file_->is_tempfile) {
631 msg_dialog.setText(tr("Save packets before merging?"));
632 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
635 * Format the message.
637 display_basename = g_filename_display_basename(cap_file_->filename);
638 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
639 g_free(display_basename);
640 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
643 msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
644 msg_dialog.setDefaultButton(QMessageBox::Save);
646 response = msg_dialog.exec();
650 case QMessageBox::Save:
651 /* Save the file but don't close it */
652 saveCaptureFile(cap_file_, FALSE);
655 case QMessageBox::Cancel:
657 /* Don't do the merge. */
664 CaptureFileDialog merge_dlg(this, cap_file_, display_filter);
666 cf_status_t merge_status;
667 char *in_filenames[2];
670 switch (prefs.gui_fileopen_style) {
672 case FO_STYLE_LAST_OPENED:
673 /* The user has specified that we should start out in the last directory
674 we looked in. If we've already opened a file, use its containing
675 directory, if we could determine it, as the directory, otherwise
676 use the "last opened" directory saved in the preferences file if
678 /* This is now the default behaviour in file_selection_new() */
681 case FO_STYLE_SPECIFIED:
682 /* The user has specified that we should always start out in a
683 specified directory; if they've specified that directory,
684 start out by showing the files in that dir. */
685 if (prefs.gui_fileopen_dir[0] != '\0')
686 merge_dlg.setDirectory(prefs.gui_fileopen_dir);
690 if (merge_dlg.merge(file_name)) {
691 if (dfilter_compile(display_filter.toUtf8().constData(), &rfcode)) {
692 cf_set_rfcode(cap_file_, rfcode);
694 /* Not valid. Tell the user, and go back and run the file
695 selection box again once they dismiss the alert. */
696 //bad_dfilter_alert_box(top_level, display_filter->str);
697 QMessageBox::warning(this, tr("Invalid Display Filter"),
698 QString(tr("The filter expression %1 isn't a valid display filter. (%2).").arg(display_filter, dfilter_error_msg)),
706 file_type = cap_file_->cd_t;
708 /* Try to merge or append the two files */
710 if (merge_dlg.mergeType() == 0) {
711 /* chronological order */
712 in_filenames[0] = cap_file_->filename;
713 in_filenames[1] = file_name.toUtf8().data();
714 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
715 } else if (merge_dlg.mergeType() <= 0) {
717 in_filenames[0] = file_name.toUtf8().data();
718 in_filenames[1] = cap_file_->filename;
719 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
722 in_filenames[0] = cap_file_->filename;
723 in_filenames[1] = file_name.toUtf8().data();
724 merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
727 if (merge_status != CF_OK) {
729 dfilter_free(rfcode);
736 /* Try to open the merged capture file. */
738 if (cf_open(&cfile, tmpname, WTAP_TYPE_AUTO, TRUE /* temporary file */, &err) != CF_OK) {
739 /* We couldn't open it; fail. */
742 dfilter_free(rfcode);
747 /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
748 it closed the previous capture file, and thus destroyed any
749 previous read filter attached to "cf"). */
750 cfile.rfcode = rfcode;
752 switch (cf_read(&cfile, FALSE)) {
756 /* Just because we got an error, that doesn't mean we were unable
757 to read any of the file; we handle what we could get from the
761 case CF_READ_ABORTED:
762 /* The user bailed out of re-reading the capture file; the
763 capture file has been closed - just free the capture file name
764 string and return (without changing the last containing
770 /* Save the name of the containing directory specified in the path name,
771 if any; we can write over cf_merged_name, which is a good thing, given that
772 "get_dirname()" does write over its argument. */
773 wsApp->setLastOpenDir(get_dirname(tmpname));
775 df_combo_box_->setEditText(display_filter);
776 main_ui_->statusBar->showExpert();
782 void MainWindow::importCaptureFile() {
783 ImportTextDialog import_dlg;
785 if (!testCaptureFileClose(FALSE, *new QString(tr(" before importing a new capture"))))
790 if (import_dlg.result() != QDialog::Accepted) {
791 main_ui_->mainStack->setCurrentWidget(main_welcome_);
795 openCaptureFile(import_dlg.capfileName());
798 void MainWindow::saveCaptureFile(capture_file *cf, bool stay_closed) {
800 gboolean discard_comments;
802 if (cf->is_tempfile) {
803 /* This is a temporary capture file, so saving it means saving
804 it to a permanent file. Prompt the user for a location
805 to which to save it. Don't require that the file format
806 support comments - if it's a temporary capture file, it's
807 probably pcap-ng, which supports comments and, if it's
808 not pcap-ng, let the user decide what they want to do
809 if they've added comments. */
810 saveAsCaptureFile(cf, FALSE, stay_closed);
812 if (cf->unsaved_changes) {
813 cf_write_status_t status;
815 /* This is not a temporary capture file, but it has unsaved
816 changes, so saving it means doing a "safe save" on top
817 of the existing file, in the same format - no UI needed
818 unless the file has comments and the file's format doesn't
821 If the file has comments, does the file's format support them?
822 If not, ask the user whether they want to discard the comments
823 or choose a different format. */
824 switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
827 /* The file can be saved in the specified format as is;
828 just drive on and save in the format they selected. */
829 discard_comments = FALSE;
832 case SAVE_WITHOUT_COMMENTS:
833 /* The file can't be saved in the specified format as is,
834 but it can be saved without the comments, and the user
835 said "OK, discard the comments", so save it in the
836 format they specified without the comments. */
837 discard_comments = TRUE;
840 case SAVE_IN_ANOTHER_FORMAT:
841 /* There are file formats in which we can save this that
842 support comments, and the user said not to delete the
843 comments. Do a "Save As" so the user can select
844 one of those formats and choose a file name. */
845 saveAsCaptureFile(cf, TRUE, stay_closed);
849 /* The user said "forget it". Just return. */
853 /* Squelch warnings that discard_comments is being used
855 g_assert_not_reached();
859 /* XXX - cf->filename might get freed out from under us, because
860 the code path through which cf_save_records() goes currently
861 closes the current file and then opens and reloads the saved file,
862 so make a copy and free it later. */
863 file_name = cf->filename;
864 status = cf_save_records(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
865 discard_comments, stay_closed);
869 /* The save succeeded; we're done.
870 If we discarded comments, redraw the packet list to reflect
871 any packets that no longer have comments. */
872 if (discard_comments)
873 packet_list_queue_draw();
875 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
876 updateForUnsavedChanges(); // we update the title bar to remove the *
881 XXX - OK, what do we do now? Let them try a
882 "Save As", in case they want to try to save to a
883 different directory r file system? */
886 case CF_WRITE_ABORTED:
887 /* The write was aborted; just drive on. */
891 /* Otherwise just do nothing. */
895 void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool stay_closed) {
896 QString file_name = "";
899 cf_write_status_t status;
901 gboolean discard_comments = FALSE;
908 CaptureFileDialog save_as_dlg(this, cf);
910 switch (prefs.gui_fileopen_style) {
912 case FO_STYLE_LAST_OPENED:
913 /* The user has specified that we should start out in the last directory
914 we looked in. If we've already opened a file, use its containing
915 directory, if we could determine it, as the directory, otherwise
916 use the "last opened" directory saved in the preferences file if
918 /* This is now the default behaviour in file_selection_new() */
921 case FO_STYLE_SPECIFIED:
922 /* The user has specified that we should always start out in a
923 specified directory; if they've specified that directory,
924 start out by showing the files in that dir. */
925 if (prefs.gui_fileopen_dir[0] != '\0')
926 save_as_dlg.setDirectory(prefs.gui_fileopen_dir);
930 /* If the file has comments, does the format the user selected
931 support them? If not, ask the user whether they want to
932 discard the comments or choose a different format. */
933 switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
936 /* The file can be saved in the specified format as is;
937 just drive on and save in the format they selected. */
938 discard_comments = FALSE;
941 case SAVE_WITHOUT_COMMENTS:
942 /* The file can't be saved in the specified format as is,
943 but it can be saved without the comments, and the user
944 said "OK, discard the comments", so save it in the
945 format they specified without the comments. */
946 discard_comments = TRUE;
949 case SAVE_IN_ANOTHER_FORMAT:
950 /* There are file formats in which we can save this that
951 support comments, and the user said not to delete the
952 comments. The combo box of file formats has had the
953 formats that don't support comments trimmed from it,
954 so run the dialog again, to let the user decide
955 whether to save in one of those formats or give up. */
956 discard_comments = FALSE;
957 must_support_comments = TRUE;
961 /* The user said "forget it". Just get rid of the dialog box
965 file_type = save_as_dlg.selectedFileType();
966 compressed = save_as_dlg.isCompressed();
968 fileAddExtension(file_name, file_type, compressed);
971 // /* If the file exists and it's user-immutable or not writable,
972 // ask the user whether they want to override that. */
973 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
974 // /* They don't. Let them try another file name or cancel. */
979 /* Attempt to save the file */
980 status = cf_save_records(cf, file_name.toUtf8().constData(), file_type, compressed,
981 discard_comments, stay_closed);
985 /* The save succeeded; we're done. */
986 /* Save the directory name for future file dialogs. */
987 dirname = get_dirname(qstring_strdup(file_name)); /* Overwrites cf_name */
988 set_last_open_dir(dirname);
990 /* If we discarded comments, redraw the packet list to reflect
991 any packets that no longer have comments. */
992 if (discard_comments)
993 packet_list_queue_draw();
995 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
996 updateForUnsavedChanges(); // we update the title bar to remove the *
1000 /* The save failed; let the user try again. */
1003 case CF_WRITE_ABORTED:
1004 /* The user aborted the save; just return. */
1011 void MainWindow::exportSelectedPackets() {
1012 QString file_name = "";
1014 gboolean compressed;
1015 packet_range_t range;
1016 cf_write_status_t status;
1018 gboolean discard_comments = FALSE;
1023 /* Init the packet range */
1024 packet_range_init(&range, cap_file_);
1025 range.process_filtered = TRUE;
1026 range.include_dependents = TRUE;
1029 CaptureFileDialog esp_dlg(this, cap_file_);
1031 switch (prefs.gui_fileopen_style) {
1033 case FO_STYLE_LAST_OPENED:
1034 /* The user has specified that we should start out in the last directory
1035 we looked in. If we've already opened a file, use its containing
1036 directory, if we could determine it, as the directory, otherwise
1037 use the "last opened" directory saved in the preferences file if
1039 /* This is now the default behaviour in file_selection_new() */
1042 case FO_STYLE_SPECIFIED:
1043 /* The user has specified that we should always start out in a
1044 specified directory; if they've specified that directory,
1045 start out by showing the files in that dir. */
1046 if (prefs.gui_fileopen_dir[0] != '\0')
1047 esp_dlg.setDirectory(prefs.gui_fileopen_dir);
1051 /* If the file has comments, does the format the user selected
1052 support them? If not, ask the user whether they want to
1053 discard the comments or choose a different format. */
1054 switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
1057 /* The file can be saved in the specified format as is;
1058 just drive on and save in the format they selected. */
1059 discard_comments = FALSE;
1062 case SAVE_WITHOUT_COMMENTS:
1063 /* The file can't be saved in the specified format as is,
1064 but it can be saved without the comments, and the user
1065 said "OK, discard the comments", so save it in the
1066 format they specified without the comments. */
1067 discard_comments = TRUE;
1070 case SAVE_IN_ANOTHER_FORMAT:
1071 /* There are file formats in which we can save this that
1072 support comments, and the user said not to delete the
1073 comments. The combo box of file formats has had the
1074 formats that don't support comments trimmed from it,
1075 so run the dialog again, to let the user decide
1076 whether to save in one of those formats or give up. */
1077 discard_comments = FALSE;
1081 /* The user said "forget it". Just get rid of the dialog box
1087 * Check that we're not going to save on top of the current
1089 * We do it here so we catch all cases ...
1090 * Unfortunately, the file requester gives us an absolute file
1091 * name and the read file name may be relative (if supplied on
1092 * the command line). From Joerg Mayer.
1094 if (files_identical(cap_file_->filename, file_name.toUtf8().constData())) {
1095 QMessageBox msg_box;
1096 gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
1098 msg_box.setIcon(QMessageBox::Critical);
1099 msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
1100 msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
1101 msg_box.setStandardButtons(QMessageBox::Ok);
1102 msg_box.setDefaultButton(QMessageBox::Ok);
1104 g_free(display_basename);
1108 file_type = esp_dlg.selectedFileType();
1109 compressed = esp_dlg.isCompressed();
1110 fileAddExtension(file_name, file_type, compressed);
1113 // /* If the file exists and it's user-immutable or not writable,
1114 // ask the user whether they want to override that. */
1115 // if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1116 // /* They don't. Let them try another file name or cancel. */
1121 /* Attempt to save the file */
1122 status = cf_export_specified_packets(cap_file_, file_name.toUtf8().constData(), &range, file_type, compressed);
1126 /* The save succeeded; we're done. */
1127 /* Save the directory name for future file dialogs. */
1128 dirname = get_dirname(qstring_strdup(file_name)); /* Overwrites cf_name */
1129 set_last_open_dir(dirname);
1131 /* If we discarded comments, redraw the packet list to reflect
1132 any packets that no longer have comments. */
1133 if (discard_comments)
1134 packet_list_queue_draw();
1137 case CF_WRITE_ERROR:
1138 /* The save failed; let the user try again. */
1141 case CF_WRITE_ABORTED:
1142 /* The user aborted the save; just return. */
1149 void MainWindow::exportDissections(export_type_e export_type) {
1150 ExportDissectionDialog ed_dlg(this, cap_file_, export_type);
1151 packet_range_t range;
1156 /* Init the packet range */
1157 packet_range_init(&range, cap_file_);
1158 range.process_filtered = TRUE;
1159 range.include_dependents = TRUE;
1164 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
1165 QString file_name_lower;
1166 QString file_suffix;
1167 GSList *extensions_list;
1168 gboolean add_extension;
1171 * Append the default file extension if there's none given by
1172 * the user or if they gave one that's not one of the valid
1173 * extensions for the file type.
1175 file_name_lower = file_name.toLower();
1176 extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
1177 if (extensions_list != NULL) {
1180 /* We have one or more extensions for this file type.
1181 Start out assuming we need to add the default one. */
1182 add_extension = TRUE;
1184 /* OK, see if the file has one of those extensions. */
1185 for (extension = extensions_list; extension != NULL;
1186 extension = g_slist_next(extension)) {
1187 file_suffix += tr(".") + (char *)extension->data;
1188 if (file_name_lower.endsWith(file_suffix)) {
1190 * The file name has one of the extensions for
1193 add_extension = FALSE;
1196 file_suffix += ".gz";
1197 if (file_name_lower.endsWith(file_suffix)) {
1199 * The file name has one of the extensions for
1202 add_extension = FALSE;
1207 /* We have no extensions for this file type. Don't add one. */
1208 add_extension = FALSE;
1210 if (add_extension) {
1211 if (wtap_default_file_extension(file_type) != NULL) {
1212 file_name += tr(".") + wtap_default_file_extension(file_type);
1220 bool MainWindow::testCaptureFileClose(bool from_quit, QString &before_what) {
1221 bool capture_in_progress = FALSE;
1223 if (!cap_file_ || cap_file_->state == FILE_CLOSED)
1224 return true; /* Already closed, nothing to do */
1227 if (cap_file_->state == FILE_READ_IN_PROGRESS) {
1228 /* This is true if we're reading a capture file *or* if we're doing
1229 a live capture. If we're reading a capture file, the main loop
1230 is busy reading packets, and only accepting input from the
1231 progress dialog, so we can't get here, so this means we're
1233 capture_in_progress = TRUE;
1237 if (prefs.gui_ask_unsaved) {
1238 if (cf_has_unsaved_data(cap_file_) || capture_in_progress) {
1239 QMessageBox msg_dialog;
1241 QPushButton *saveButton;
1242 QPushButton *discardButton;
1244 msg_dialog.setIcon(QMessageBox::Question);
1245 msg_dialog.setWindowTitle("Unsaved packets...");
1246 /* This file has unsaved data or there's a capture in
1247 progress; ask the user whether to save the data. */
1248 if (cap_file_->is_tempfile) {
1250 msg_dialog.setText(tr("You have unsaved packets"));
1251 msg_dialog.setInformativeText(tr("They will be lost if you don't save them."));
1253 if (capture_in_progress) {
1254 question.append(tr("Do you want to stop the capture and save the captured packets"));
1256 question.append(tr("Do you want to save the captured packets"));
1258 question.append(before_what).append(tr("?"));
1259 msg_dialog.setInformativeText(question);
1264 * Format the message.
1266 if (capture_in_progress) {
1267 question.append(tr("Do you want to stop the capture and save the captured packets"));
1268 question.append(before_what).append(tr("?"));
1269 msg_dialog.setText(question);
1270 msg_dialog.setInformativeText(tr("Your captured packets will be lost if you don't save them."));
1272 gchar *display_basename = g_filename_display_basename(cap_file_->filename);
1273 question.append(QString(tr("Do you want to save the changes you've made to the capture file \"%1\"%2?"))
1274 .arg(display_basename)
1277 g_free(display_basename);
1278 msg_dialog.setText(question);
1279 msg_dialog.setInformativeText(tr("Your changes will be lost if you don't save them."));
1283 // XXX Text comes from ui/gtk/stock_icons.[ch]
1284 // Note that the button roles differ from the GTK+ version.
1285 // Cancel = RejectRole
1286 // Save = AcceptRole
1287 // Don't Save = DestructiveRole
1288 msg_dialog.addButton(QMessageBox::Cancel);
1290 if (capture_in_progress) {
1291 saveButton = msg_dialog.addButton(tr("Stop and Save"), QMessageBox::AcceptRole);
1293 saveButton = msg_dialog.addButton(QMessageBox::Save);
1295 msg_dialog.setDefaultButton(saveButton);
1298 if (cap_file_->state == FILE_READ_IN_PROGRESS) {
1299 discardButton = msg_dialog.addButton(tr("Stop and Quit without Saving"),
1300 QMessageBox::DestructiveRole);
1302 discardButton = msg_dialog.addButton(tr("Quit without Saving"),
1303 QMessageBox::DestructiveRole);
1306 if (capture_in_progress) {
1307 discardButton = msg_dialog.addButton(tr("Stop and Continue without Saving"),
1308 QMessageBox::DestructiveRole);
1310 discardButton = msg_dialog.addButton(tr("Continue &without Saving"), QMessageBox::DestructiveRole);
1315 /* According to the Qt doc:
1316 * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1318 * Therefore we should use clickedButton() to determine which button was clicked. */
1320 if(msg_dialog.clickedButton() == saveButton)
1323 /* If there's a capture in progress, we have to stop the capture
1324 and then do the save. */
1325 if (capture_in_progress)
1328 /* Save the file and close it */
1329 saveCaptureFile(cap_file_, TRUE);
1331 else if(msg_dialog.clickedButton() == discardButton)
1335 * If there's a capture in progress; we have to stop the capture
1336 * and then do the close.
1338 if (capture_in_progress)
1341 /* Just close the file, discarding changes */
1342 cf_close(cap_file_);
1345 else //cancelButton or some other unspecified button
1351 /* Unchanged file, just close it */
1352 cf_close(cap_file_);
1355 /* User asked not to be bothered by those prompts, just close it.
1356 XXX - should that apply only to saving temporary files? */
1358 /* If there's a capture in progress, we have to stop the capture
1359 and then do the close. */
1360 if (capture_in_progress)
1363 cf_close(cap_file_);
1366 return true; /* File closed */
1369 void MainWindow::captureStop() {
1372 while(cap_file_ && cap_file_->state == FILE_READ_IN_PROGRESS) {
1373 WiresharkApplication::processEvents();
1377 void MainWindow::initMainToolbarIcons()
1379 #if defined(Q_OS_WIN)
1380 // Current GTK+ and other Windows app behavior.
1381 main_ui_->mainToolBar->setIconSize(QSize(16, 16));
1383 // Force icons to 24x24 for now, otherwise actionFileOpen looks wonky.
1384 main_ui_->mainToolBar->setIconSize(QSize(24, 24));
1387 // Toolbar actions. The GNOME HIG says that we should have a menu icon for each
1388 // toolbar item but that clutters up our menu. Set menu icons sparingly.
1390 main_ui_->actionCaptureStart->setIcon(StockIcon("x-capture-start"));
1391 main_ui_->actionCaptureStop->setIcon(StockIcon("x-capture-stop"));
1392 main_ui_->actionCaptureRestart->setIcon(StockIcon("x-capture-restart"));
1393 main_ui_->actionCaptureOptions->setIcon(StockIcon("x-capture-options"));
1395 // Menu icons are disabled in main_window.ui for these items.
1396 main_ui_->actionFileOpen->setIcon(StockIcon("document-open"));
1397 main_ui_->actionFileSave->setIcon(StockIcon("x-capture-file-save"));
1398 main_ui_->actionFileClose->setIcon(StockIcon("x-capture-file-close"));
1399 // main_ui_->actionViewReload->setIcon(StockIcon("x-capture-file-reload"));
1401 main_ui_->actionEditFindPacket->setIcon(StockIcon("edit-find"));
1402 main_ui_->actionGoPreviousPacket->setIcon(StockIcon("go-previous"));
1403 main_ui_->actionGoNextPacket->setIcon(StockIcon("go-next"));
1404 main_ui_->actionGoGoToPacket->setIcon(StockIcon("go-jump"));
1405 main_ui_->actionGoFirstPacket->setIcon(StockIcon("go-first"));
1406 main_ui_->actionGoLastPacket->setIcon(StockIcon("go-last"));
1408 main_ui_->actionViewColorizePacketList->setIcon(StockIcon("x-colorize-packets"));
1409 main_ui_->actionViewColorizePacketList->setChecked(recent.packet_list_colorize);
1410 // main_ui_->actionViewAutoScroll->setIcon(StockIcon("x-stay-last"));
1412 QList<QKeySequence> zi_seq = main_ui_->actionViewZoomIn->shortcuts();
1413 zi_seq << QKeySequence(Qt::CTRL + Qt::Key_Equal);
1414 main_ui_->actionViewZoomIn->setIcon(StockIcon("zoom-in"));
1415 main_ui_->actionViewZoomIn->setShortcuts(zi_seq);
1416 main_ui_->actionViewZoomOut->setIcon(StockIcon("zoom-out"));
1417 main_ui_->actionViewNormalSize->setIcon(StockIcon("zoom-original"));
1418 main_ui_->actionViewResizeColumns->setIcon(StockIcon("x-resize-columns"));
1421 void MainWindow::initShowHideMainWidgets()
1423 if (show_hide_actions_) {
1427 show_hide_actions_ = new QActionGroup(this);
1428 QMap<QAction *, QWidget *> shmw_actions;
1430 show_hide_actions_->setExclusive(false);
1431 shmw_actions[main_ui_->actionViewMainToolbar] = main_ui_->mainToolBar;
1432 shmw_actions[main_ui_->actionViewFilterToolbar] = main_ui_->displayFilterToolBar;
1433 shmw_actions[main_ui_->actionViewWirelessToolbar] = NULL; // Doesn't exist yet.
1434 shmw_actions[main_ui_->actionViewStatusBar] = main_ui_->statusBar;
1435 shmw_actions[main_ui_->actionViewPacketList] = packet_list_;
1436 shmw_actions[main_ui_->actionViewPacketDetails] = proto_tree_;
1437 shmw_actions[main_ui_->actionViewPacketBytes] = byte_view_tab_;
1439 main_ui_->actionViewMainToolbar->setChecked(recent.main_toolbar_show);
1440 main_ui_->actionViewFilterToolbar->setChecked(recent.filter_toolbar_show);
1441 main_ui_->actionViewWirelessToolbar->setChecked(recent.wireless_toolbar_show);
1442 main_ui_->actionViewStatusBar->setChecked(recent.statusbar_show);
1443 main_ui_->actionViewPacketList->setChecked(recent.packet_list_show);
1444 main_ui_->actionViewPacketDetails->setChecked(recent.tree_view_show);
1445 main_ui_->actionViewPacketBytes->setChecked(recent.byte_view_show);
1447 foreach (QAction *shmwa, shmw_actions.keys()) {
1448 shmwa->setData(qVariantFromValue(shmw_actions[shmwa]));
1449 show_hide_actions_->addAction(shmwa);
1450 showHideMainWidgets(shmwa);
1453 connect(show_hide_actions_, SIGNAL(triggered(QAction*)), this, SLOT(showHideMainWidgets(QAction*)));
1456 Q_DECLARE_METATYPE(ts_type)
1458 void MainWindow::initTimeDisplayFormatMenu()
1460 if (time_display_actions_) {
1464 time_display_actions_ = new QActionGroup(this);
1465 QMap<QAction *, ts_type> td_actions;
1467 td_actions[main_ui_->actionViewTimeDisplayFormatDateYMDandTimeOfDay] = TS_ABSOLUTE_WITH_YMD;
1468 td_actions[main_ui_->actionViewTimeDisplayFormatDateYDOYandTimeOfDay] = TS_ABSOLUTE_WITH_YDOY;
1469 td_actions[main_ui_->actionViewTimeDisplayFormatTimeOfDay] = TS_ABSOLUTE;
1470 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceEpoch] = TS_EPOCH;
1471 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceBeginningOfCapture] = TS_RELATIVE;
1472 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousCapturedPacket] = TS_DELTA;
1473 td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousDisplayedPacket] = TS_DELTA_DIS;
1474 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYMDandTimeOfDay] = TS_UTC_WITH_YMD;
1475 td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYDOYandTimeOfDay] = TS_UTC_WITH_YDOY;
1476 td_actions[main_ui_->actionViewTimeDisplayFormatUTCTimeOfDay] = TS_UTC;
1478 foreach (QAction* tda, td_actions.keys()) {
1479 tda->setData(qVariantFromValue(td_actions[tda]));
1480 time_display_actions_->addAction(tda);
1481 if (recent.gui_time_format == td_actions[tda]) {
1482 tda->setChecked(true);
1486 connect(time_display_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampFormat(QAction*)));
1488 main_ui_->actionViewTimeDisplaySecondsWithHoursAndMinutes->setChecked(recent.gui_seconds_format == TS_SECONDS_HOUR_MIN_SEC);
1491 Q_DECLARE_METATYPE(ts_precision)
1493 void MainWindow::initTimePrecisionFormatMenu()
1495 if (time_precision_actions_) {
1499 time_precision_actions_ = new QActionGroup(this);
1500 QMap<QAction *, ts_precision> tp_actions;
1501 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionAutomatic] = TS_PREC_AUTO;
1502 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionSeconds] = TS_PREC_FIXED_SEC;
1503 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionDeciseconds] = TS_PREC_FIXED_DSEC;
1504 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionCentiseconds] = TS_PREC_FIXED_CSEC;
1505 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMilliseconds] = TS_PREC_FIXED_MSEC;
1506 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMicroseconds] = TS_PREC_FIXED_USEC;
1507 tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionNanoseconds] = TS_PREC_FIXED_NSEC;
1509 foreach (QAction* tpa, tp_actions.keys()) {
1510 tpa->setData(qVariantFromValue(tp_actions[tpa]));
1511 time_precision_actions_->addAction(tpa);
1512 if (recent.gui_time_precision == tp_actions[tpa]) {
1513 tpa->setChecked(true);
1517 connect(time_precision_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampPrecision(QAction*)));
1521 void MainWindow::setTitlebarForCaptureFile()
1523 if (cap_file_ && cap_file_->filename) {
1525 // Qt *REALLY* doesn't like windows that sometimes have a
1526 // title set with setWindowTitle() and other times have a
1527 // file path set; apparently, once you've set the title
1528 // with setWindowTitle(), it sticks, and setWindowFilePath()
1529 // has no effect. It appears to can clear the title with
1530 // setWindowTitle(NULL), but that clears the actual title in
1531 // the title bar, and setWindowFilePath() then, I guess, sees
1532 // that there's already a file path, and does nothing, leaving
1533 // the title bar empty. So you then have to clear the file path
1534 // with setWindowFilePath(NULL), and then set it.
1536 // Maybe there's a #include "you're holding it wrong" here.
1537 // However, I really don't want to hear from people who think
1538 // that a window can never be associated with something other
1539 // than a user file at time T1 and with a user file at time T2,
1540 // given that, in Wireshark, a window can be associated with a
1541 // live capture at time T1 and then, after you've saved the live
1542 // capture to a user file, associated with a user file at time T2.
1544 if (cap_file_->is_tempfile) {
1546 // For a temporary file, put the source of the data
1547 // in the window title, not whatever random pile
1548 // of characters is the last component of the path
1551 // XXX - on non-Mac platforms, put in the application
1555 setWindowFilePath(NULL);
1556 window_name = g_strdup_printf("Capturing from %s[*]", cf_get_tempfile_source(cap_file_)); //TODO : Fix Translate
1557 setWindowTitle(window_name);
1558 g_free(window_name);
1561 // For a user file, set the full path; that way,
1562 // for OS X, it'll set the "proxy icon". Qt
1563 // handles extracting the last component.
1565 // Sadly, some UN*Xes don't necessarily use UTF-8
1566 // for their file names, so we have to map the
1567 // file path to UTF-8. If that fails, we're somewhat
1570 char *utf8_filename = g_filename_to_utf8(cap_file_->filename,
1575 if (utf8_filename == NULL) {
1576 // So what the heck else can we do here?
1577 setWindowTitle(tr("(File name can't be mapped to UTF-8)"));
1579 setWindowTitle(NULL);
1580 setWindowFilePath(NULL);
1581 setWindowFilePath(utf8_filename);
1582 g_free(utf8_filename);
1585 setWindowModified(cf_has_unsaved_data(cap_file_));
1587 /* We have no capture file. */
1588 setWindowFilePath(NULL);
1589 setWindowTitle(tr("The Wireshark Network Analyzer"));
1593 void MainWindow::setTitlebarForSelectedTreeRow()
1595 setWindowTitle(tr("The Wireshark Network Analyzer"));
1599 void MainWindow::setTitlebarForCaptureInProgress()
1603 setWindowFilePath(NULL);
1605 window_name = g_strdup_printf("Capturing from %s", cf_get_tempfile_source(cap_file_)); //TODO : Fix Translate
1606 setWindowTitle(window_name);
1607 g_free(window_name);
1609 /* We have no capture in progress. */
1610 setWindowTitle(tr("The Wireshark Network Analyzer"));
1616 void MainWindow::setMenusForFollowStream()
1618 gboolean is_tcp = FALSE, is_udp = FALSE;
1623 if (!cap_file_->edt)
1626 main_ui_->actionAnalyzeFollowTCPStream->setEnabled(false);
1627 main_ui_->actionAnalyzeFollowUDPStream->setEnabled(false);
1628 main_ui_->actionAnalyzeFollowSSLStream->setEnabled(false);
1630 proto_get_frame_protocols(cap_file_->edt->pi.layers, NULL, &is_tcp, &is_udp, NULL, NULL);
1634 main_ui_->actionAnalyzeFollowTCPStream->setEnabled(true);
1639 main_ui_->actionAnalyzeFollowUDPStream->setEnabled(true);
1642 if ( epan_dissect_packet_contains_field(cap_file_->edt, "ssl") )
1644 main_ui_->actionAnalyzeFollowSSLStream->setEnabled(true);
1648 /* Enable or disable menu items based on whether you have a capture file
1649 you've finished reading and, if you have one, whether it's been saved
1650 and whether it could be saved except by copying the raw packet data. */
1651 void MainWindow::setMenusForCaptureFile(bool force_disable)
1653 if (force_disable || cap_file_ == NULL || cap_file_->state == FILE_READ_IN_PROGRESS) {
1654 /* We have no capture file or we're currently reading a file */
1655 main_ui_->actionFileMerge->setEnabled(false);
1656 main_ui_->actionFileClose->setEnabled(false);
1657 main_ui_->actionFileSave->setEnabled(false);
1658 main_ui_->actionFileSaveAs->setEnabled(false);
1659 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1660 main_ui_->actionFileExportPackets->setEnabled(false);
1661 main_ui_->menuFileExportPacketDissections->setEnabled(false);
1662 main_ui_->actionFileExportPacketBytes->setEnabled(false);
1663 main_ui_->actionFileExportPDU->setEnabled(false);
1664 main_ui_->actionFileExportSSLSessionKeys->setEnabled(false);
1665 main_ui_->menuFileExportObjects->setEnabled(false);
1666 main_ui_->actionViewReload->setEnabled(false);
1668 main_ui_->actionFileMerge->setEnabled(cf_can_write_with_wiretap(cap_file_));
1670 main_ui_->actionFileClose->setEnabled(true);
1671 main_ui_->actionFileSave->setEnabled(cf_can_save(cap_file_));
1672 main_ui_->actionFileSaveAs->setEnabled(cf_can_save_as(cap_file_));
1673 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(true);
1675 * "Export Specified Packets..." should be available only if
1676 * we can write the file out in at least one format.
1678 main_ui_->actionFileExportPackets->setEnabled(cf_can_write_with_wiretap(cap_file_));
1679 main_ui_->menuFileExportPacketDissections->setEnabled(true);
1680 main_ui_->actionFileExportPacketBytes->setEnabled(true);
1681 main_ui_->actionFileExportPDU->setEnabled(true);
1682 main_ui_->actionFileExportSSLSessionKeys->setEnabled(true);
1683 main_ui_->menuFileExportObjects->setEnabled(true);
1684 main_ui_->actionViewReload->setEnabled(true);
1688 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
1689 /* Either a capture was started or stopped; in either case, it's not
1690 in the process of stopping, so allow quitting. */
1692 main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
1693 main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
1694 main_ui_->menuFileExportPacketDissections->setEnabled(capture_in_progress);
1695 main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
1696 main_ui_->actionFileExportPDU->setEnabled(capture_in_progress);
1697 main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
1698 main_ui_->menuFileExportObjects->setEnabled(capture_in_progress);
1699 main_ui_->menuFileSet->setEnabled(!capture_in_progress);
1700 main_ui_->actionFileQuit->setEnabled(true);
1702 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(capture_in_progress);
1704 // XXX Fix packet list heading menu sensitivity
1705 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
1706 // !capture_in_progress);
1707 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
1708 // !capture_in_progress);
1709 // set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
1710 // !capture_in_progress);
1713 main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
1714 main_ui_->actionCaptureStart->setEnabled(!capture_in_progress);
1715 main_ui_->actionCaptureStart->setChecked(capture_in_progress);
1716 main_ui_->actionCaptureStop->setEnabled(capture_in_progress);
1717 main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
1718 #endif /* HAVE_LIBPCAP */
1722 void MainWindow::setMenusForCaptureStopping() {
1723 main_ui_->actionFileQuit->setEnabled(false);
1724 main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1726 main_ui_->actionCaptureStart->setChecked(false);
1727 main_ui_->actionCaptureStop->setEnabled(false);
1728 main_ui_->actionCaptureRestart->setEnabled(false);
1729 #endif /* HAVE_LIBPCAP */
1732 void MainWindow::setForCapturedPackets(bool have_captured_packets)
1734 main_ui_->actionFilePrint->setEnabled(have_captured_packets);
1736 // set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
1737 // have_captured_packets);
1739 main_ui_->actionEditFindPacket->setEnabled(have_captured_packets);
1740 main_ui_->actionEditFindNext->setEnabled(have_captured_packets);
1741 main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets);
1743 main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
1744 main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
1745 main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
1746 main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
1747 main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
1749 main_ui_->actionViewZoomIn->setEnabled(have_captured_packets);
1750 main_ui_->actionViewZoomOut->setEnabled(have_captured_packets);
1751 main_ui_->actionViewNormalSize->setEnabled(have_captured_packets);
1752 main_ui_->actionViewResizeColumns->setEnabled(have_captured_packets);
1754 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/PreviousPacketInConversation",
1755 // have_captured_packets);
1756 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/NextPacketInConversation",
1757 // have_captured_packets);
1758 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/StatisticsMenu/Summary",
1759 // have_captured_packets);
1760 // set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/StatisticsMenu/ProtocolHierarchy",
1761 // have_captured_packets);
1762 main_ui_->actionStatisticsIOGraph->setEnabled(have_captured_packets);
1765 void MainWindow::setMenusForFileSet(bool enable_list_files) {
1766 bool enable_next = fileset_get_next() != NULL && enable_list_files;
1767 bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
1769 main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
1770 main_ui_->actionFileSetNextFile->setEnabled(enable_next);
1771 main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
1774 void MainWindow::updateForUnsavedChanges() {
1775 setTitlebarForCaptureFile();
1776 setMenusForCaptureFile();
1779 void MainWindow::changeEvent(QEvent* event)
1783 switch (event->type())
1785 case QEvent::LanguageChange:
1786 main_ui_->retranslateUi(this);
1788 case QEvent::LocaleChange:{
1789 QString locale = QLocale::system().name();
1790 locale.truncate(locale.lastIndexOf('_'));
1791 wsApp->loadLanguage(locale);
1798 QMainWindow::changeEvent(event);
1801 /* Update main window items based on whether there's a capture in progress. */
1802 void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
1804 setMenusForCaptureInProgress(capture_in_progress);
1806 //#ifdef HAVE_LIBPCAP
1807 // set_toolbar_for_capture_in_progress(capture_in_progress);
1809 // set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
1819 * indent-tabs-mode: nil
1822 * ex: set shiftwidth=4 tabstop=8 expandtab:
1823 * :indentSize=4:tabSize=8:noTabs=true: