3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * SPDX-License-Identifier: GPL-2.0-or-later
12 #include <epan/prefs.h>
14 #include "ui/capture_globals.h"
17 #include "wsutil/filesystem.h"
18 #include "wsutil/version_info.h"
20 #include "welcome_page.h"
21 #include <ui_welcome_page.h>
22 #include <ui/qt/utils/tango_colors.h>
23 #include <ui/qt/utils/color_utils.h>
24 #include <ui/qt/utils/qt_ui_utils.h>
25 #include "main_application.h"
29 #include <QDesktopServices>
31 #include <QListWidget>
33 #include <QResizeEvent>
37 #ifndef VERSION_FLAVOR
38 #define VERSION_FLAVOR ""
43 WelcomePage::WelcomePage(QWidget
*parent
) :
45 welcome_ui_(new Ui::WelcomePage
),
46 flavor_(tr(VERSION_FLAVOR
)),
48 show_in_str_(tr("Show in Finder")),
50 show_in_str_(tr("Show in Folder")),
55 welcome_ui_
->setupUi(this);
57 recent_files_
= welcome_ui_
->recentList
;
59 welcome_ui_
->captureFilterComboBox
->setEnabled(false);
61 welcome_ui_
->mainWelcomeBanner
->setText(tr("Welcome to %1").arg(mainApp
->applicationName()));
67 recent_files_
->setAttribute(Qt::WA_MacShowFocusRect
, false);
70 welcome_ui_
->openFrame
->hide();
71 recent_files_
->setTextElideMode(Qt::ElideLeft
);
73 welcome_ui_
->recentList
->setContextMenuPolicy(Qt::CustomContextMenu
);
74 connect(recent_files_
, &QListWidget::customContextMenuRequested
, this, &WelcomePage::showRecentContextMenu
);
76 connect(mainApp
, &MainApplication::updateRecentCaptureStatus
, this, &WelcomePage::updateRecentCaptures
);
77 connect(mainApp
, &MainApplication::preferencesChanged
, this, &WelcomePage::updateRecentCaptures
);
78 connect(mainApp
, &MainApplication::appInitialized
, this, &WelcomePage::appInitialized
);
79 connect(mainApp
, &MainApplication::localInterfaceListChanged
, this, &WelcomePage::interfaceListChanged
);
81 connect(mainApp
, &MainApplication::scanLocalInterfaces
,
82 welcome_ui_
->interfaceFrame
, &InterfaceFrame::scanLocalInterfaces
);
84 connect(welcome_ui_
->interfaceFrame
, &InterfaceFrame::itemSelectionChanged
,
85 welcome_ui_
->captureFilterComboBox
, &CaptureFilterCombo::interfacesChanged
);
86 connect(welcome_ui_
->interfaceFrame
, &InterfaceFrame::typeSelectionChanged
,
87 this, &WelcomePage::interfaceListChanged
);
88 connect(welcome_ui_
->interfaceFrame
, &InterfaceFrame::itemSelectionChanged
, this, &WelcomePage::interfaceSelected
);
89 connect(welcome_ui_
->captureFilterComboBox
->lineEdit(), &QLineEdit::textEdited
,
90 this, &WelcomePage::captureFilterTextEdited
);
91 connect(welcome_ui_
->captureFilterComboBox
, &CaptureFilterCombo::captureFilterSyntaxChanged
,
92 this, &WelcomePage::captureFilterSyntaxChanged
);
93 connect(welcome_ui_
->captureFilterComboBox
, &CaptureFilterCombo::startCapture
,
94 this, &WelcomePage::captureStarting
);
95 connect(recent_files_
, &QListWidget::itemActivated
, this, &WelcomePage::openRecentItem
);
96 updateRecentCaptures();
98 splash_overlay_
= new SplashOverlay(this);
101 WelcomePage::~WelcomePage()
106 InterfaceFrame
*WelcomePage::getInterfaceFrame()
108 return welcome_ui_
->interfaceFrame
;
111 const QString
WelcomePage::captureFilter()
113 return welcome_ui_
->captureFilterComboBox
->currentText();
116 void WelcomePage::setCaptureFilter(const QString capture_filter
)
118 // capture_filter comes from the current filter in
119 // CaptureInterfacesDialog. We need to find a good way to handle
121 welcome_ui_
->captureFilterComboBox
->lineEdit()->setText(capture_filter
);
124 void WelcomePage::interfaceListChanged()
126 QString btnText
= tr("All interfaces shown");
127 if (welcome_ui_
->interfaceFrame
->interfacesHidden() > 0) {
128 btnText
= tr("%n interface(s) shown, %1 hidden", "",
129 welcome_ui_
->interfaceFrame
->interfacesPresent())
130 .arg(welcome_ui_
->interfaceFrame
->interfacesHidden());
132 welcome_ui_
->btnInterfaceType
->setText(btnText
);
133 welcome_ui_
->btnInterfaceType
->setMenu(welcome_ui_
->interfaceFrame
->getSelectionMenu());
136 void WelcomePage::setReleaseLabel()
138 // XXX Add a "check for updates" link?
139 QString full_release
;
140 QDate today
= QDate::currentDate();
141 if ((today
.month() == 4 && today
.day() == 1) || (today
.month() == 7 && today
.day() == 14)) {
142 if (is_packet_configuration_namespace()) {
143 full_release
= tr("You are sniffing the glue that holds the Internet together using Wireshark ");
145 full_release
= tr("You are sniffing the glue that holds your system together using Stratoshark ");
148 if (is_packet_configuration_namespace()) {
149 full_release
= tr("You are running Wireshark ");
151 full_release
= tr("You are running Stratoshark ");
154 full_release
+= is_packet_configuration_namespace() ? get_ws_vcs_version_info() : get_ss_vcs_version_info();
156 #ifdef HAVE_SOFTWARE_UPDATE
157 if (prefs
.gui_update_enabled
) {
158 full_release
+= tr(" You receive automatic updates.");
160 full_release
+= tr(" You have disabled automatic updates.");
163 // XXX Is there a way to tell if the user installed Wireshark via an
164 // external package manager? If so we could say so here. We could
165 // also add a link to the download page.
167 welcome_ui_
->fullReleaseLabel
->setText(full_release
);
170 void WelcomePage::appInitialized()
175 welcome_ui_
->captureFilterComboBox
->lineEdit()->setText(global_capture_opts
.default_options
.cfilter
);
176 #endif // HAVE_LIBPCAP
178 welcome_ui_
->captureFilterComboBox
->setEnabled(true);
180 interfaceListChanged();
182 welcome_ui_
->interfaceFrame
->ensureSelectedInterface();
184 delete splash_overlay_
;
185 splash_overlay_
= NULL
;
189 // Update each selected device cfilter when the user changes the contents
190 // of the capture filter lineedit. We do so here so that we don't clobber
191 // filters set in the Capture Options / Interfaces dialog or ones set via
193 void WelcomePage::captureFilterTextEdited(const QString capture_filter
)
195 if (global_capture_opts
.num_selected
> 0) {
198 for (unsigned i
= 0; i
< global_capture_opts
.all_ifaces
->len
; i
++) {
199 device
= &g_array_index(global_capture_opts
.all_ifaces
, interface_t
, i
);
200 if (!device
->selected
) {
203 // if (device->active_dlt == -1) {
204 // simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The link type of interface %s was not specified.", device->name);
205 // continue; /* Programming error: somehow managed to select an "unsupported" entry */
207 g_free(device
->cfilter
);
208 if (capture_filter
.isEmpty()) {
209 device
->cfilter
= NULL
;
211 device
->cfilter
= qstring_strdup(capture_filter
);
213 // update_filter_string(device->name, filter_text);
218 // No-op if we don't have capturing.
219 void WelcomePage::captureFilterTextEdited(const QString
)
224 // The interface list selection has changed. At this point the user might
225 // have entered a filter or we might have pre-filled one from a number of
226 // sources such as our remote connection, the command line, or a previous
228 // Must not change any interface data.
229 void WelcomePage::interfaceSelected()
231 QPair
<const QString
, bool> sf_pair
= CaptureFilterEdit::getSelectedFilter();
232 const QString user_filter
= sf_pair
.first
;
233 bool conflict
= sf_pair
.second
;
236 welcome_ui_
->captureFilterComboBox
->lineEdit()->clear();
237 welcome_ui_
->captureFilterComboBox
->setConflict(true);
239 welcome_ui_
->captureFilterComboBox
->lineEdit()->setText(user_filter
);
242 // Notify others (capture options dialog) that the selection has changed.
243 emit
interfacesChanged();
246 bool WelcomePage::event(QEvent
*event
)
248 switch (event
->type()) {
249 case QEvent::ApplicationPaletteChange
:
256 return QFrame::event(event
);
259 void WelcomePage::on_interfaceFrame_showExtcapOptions(QString device_name
, bool startCaptureOnClose
)
261 emit
showExtcapOptions(device_name
, startCaptureOnClose
);
264 void WelcomePage::on_interfaceFrame_startCapture(QStringList ifaces
)
266 emit
startCapture(ifaces
);
269 void WelcomePage::captureStarting()
271 welcome_ui_
->interfaceFrame
->ensureSelectedInterface();
272 emit
startCapture(QStringList());
275 void WelcomePage::updateRecentCaptures() {
277 QListWidgetItem
*rfItem
;
279 QString selectedFilename
;
281 if (!recent_files_
->selectedItems().isEmpty()) {
282 rfItem
= recent_files_
->selectedItems().first();
283 selectedFilename
= rfItem
->data(Qt::UserRole
).toString();
286 if (mainApp
->recentItems().count() == 0 || prefs
.gui_welcome_page_show_recent
) {
287 // Recent menu has been cleared, remove all recent files.
288 while (recent_files_
->count()) {
289 delete recent_files_
->item(0);
294 if(prefs
.gui_welcome_page_show_recent
) {
295 foreach (recent_item_status
*ri
, mainApp
->recentItems()) {
296 itemLabel
= ri
->filename
;
298 if (rfRow
>= recent_files_
->count()) {
299 recent_files_
->addItem(itemLabel
);
302 itemLabel
.append(" (");
303 if (ri
->accessible
) {
304 if (ri
->size
/1024/1024/1024 > 10) {
305 itemLabel
.append(QStringLiteral("%1 GB").arg(ri
->size
/1024/1024/1024));
306 } else if (ri
->size
/1024/1024 > 10) {
307 itemLabel
.append(QStringLiteral("%1 MB").arg(ri
->size
/1024/1024));
308 } else if (ri
->size
/1024 > 10) {
309 itemLabel
.append(QStringLiteral("%1 KB").arg(ri
->size
/1024));
311 itemLabel
.append(QStringLiteral("%1 Bytes").arg(ri
->size
));
314 itemLabel
.append(tr("not found"));
316 itemLabel
.append(")");
317 rfFont
.setItalic(!ri
->accessible
);
318 rfItem
= recent_files_
->item(rfRow
);
319 rfItem
->setText(itemLabel
);
320 rfItem
->setData(Qt::AccessibleTextRole
, itemLabel
);
321 rfItem
->setData(Qt::UserRole
, ri
->filename
);
322 rfItem
->setFlags(ri
->accessible
? Qt::ItemIsSelectable
| Qt::ItemIsEnabled
: Qt::NoItemFlags
);
323 rfItem
->setFont(rfFont
);
324 if (ri
->filename
== selectedFilename
) {
325 rfItem
->setSelected(true);
331 int row
= recent_files_
->count();
332 while (row
> 0 && (row
> (int) prefs
.gui_recent_files_count_max
|| row
> rfRow
)) {
334 delete recent_files_
->item(row
);
336 if (recent_files_
->count() > 0) {
337 welcome_ui_
->openFrame
->animatedShow();
339 welcome_ui_
->openFrame
->animatedHide();
343 void WelcomePage::openRecentItem(QListWidgetItem
*item
) {
344 QString cfPath
= item
->data(Qt::UserRole
).toString();
345 emit
recentFileActivated(cfPath
);
348 void WelcomePage::resizeEvent(QResizeEvent
*event
)
351 splash_overlay_
->resize(event
->size());
354 QFrame::resizeEvent(event
);
357 void WelcomePage::setCaptureFilterText(const QString capture_filter
)
359 welcome_ui_
->captureFilterComboBox
->lineEdit()->setText(capture_filter
);
360 captureFilterTextEdited(capture_filter
);
363 void WelcomePage::changeEvent(QEvent
* event
)
367 switch (event
->type())
369 case QEvent::LanguageChange
:
370 welcome_ui_
->retranslateUi(this);
371 welcome_ui_
->flavorBanner
->setText(flavor_
);
372 interfaceListChanged();
379 QFrame::changeEvent(event
);
382 void WelcomePage::showRecentContextMenu(QPoint pos
)
384 QListWidgetItem
*li
= recent_files_
->itemAt(pos
);
387 QMenu
*recent_ctx_menu
= new QMenu(this);
388 recent_ctx_menu
->setAttribute(Qt::WA_DeleteOnClose
);
390 QString cf_path
= li
->data(Qt::UserRole
).toString();
392 QAction
*show_action
= recent_ctx_menu
->addAction(show_in_str_
);
393 show_action
->setData(cf_path
);
394 connect(show_action
, &QAction::triggered
, this, &WelcomePage::showRecentFolder
);
396 QAction
*copy_action
= recent_ctx_menu
->addAction(tr("Copy file path"));
397 copy_action
->setData(cf_path
);
398 connect(copy_action
, &QAction::triggered
, this, &WelcomePage::copyRecentPath
);
400 recent_ctx_menu
->addSeparator();
402 QAction
*remove_action
= recent_ctx_menu
->addAction(tr("Remove from list"));
403 remove_action
->setData(cf_path
);
404 connect(remove_action
, &QAction::triggered
, this, &WelcomePage::removeRecentPath
);
406 recent_ctx_menu
->popup(recent_files_
->mapToGlobal(pos
));
409 void WelcomePage::showRecentFolder()
411 QAction
*ria
= qobject_cast
<QAction
*>(sender());
414 QString cf_path
= ria
->data().toString();
415 if (cf_path
.isEmpty()) return;
417 desktop_show_in_folder(cf_path
);
420 void WelcomePage::copyRecentPath()
422 QAction
*ria
= qobject_cast
<QAction
*>(sender());
425 QString cf_path
= ria
->data().toString();
426 if (cf_path
.isEmpty()) return;
428 mainApp
->clipboard()->setText(cf_path
);
431 void WelcomePage::removeRecentPath()
433 QAction
*ria
= qobject_cast
<QAction
*>(sender());
436 QString cf_path
= ria
->data().toString();
437 if (cf_path
.isEmpty()) return;
439 mainApp
->removeRecentItem(cf_path
);
442 void WelcomePage::on_captureLabel_clicked()
444 mainApp
->doTriggerMenuItem(MainApplication::CaptureOptionsDialog
);
447 void WelcomePage::on_helpLabel_clicked()
449 QDesktopServices::openUrl(QUrl(WS_DOCS_URL
));
452 void WelcomePage::updateStyleSheets()
454 QString welcome_ss
= QString(
458 "WelcomePage, QAbstractItemView {"
459 " background-color: palette(base);"
460 " color: palette(text);"
462 "QAbstractItemView {"
466 #if !defined(Q_OS_WIN)
467 welcome_ss
+= QString(
468 "QAbstractItemView:item:hover {"
469 " background-color: %1;"
470 " color: palette(text);"
473 .arg(ColorUtils::hoverBackground().name(QColor::HexArgb
));
475 setStyleSheet(welcome_ss
);
477 QString banner_ss
= QString(
479 " border-radius: 0.33em;"
481 " background-color: %2;"
485 .arg(QColor(tango_aluminium_6
).name()) // Text color
486 .arg(QColor(tango_sky_blue_2
).name()); // Background color
487 welcome_ui_
->mainWelcomeBanner
->setStyleSheet(banner_ss
);
489 QString title_button_ss
= QString(
497 .arg(QColor(tango_aluminium_4
).name()) // Text color
498 .arg(QColor(tango_sky_blue_4
).name()); // Hover color
500 // XXX Is there a better term than "flavor"? Provider? Admonition (a la DocBook)?
502 // Typical use cases are automated builds from wireshark.org and private,
503 // not-for-redistribution packages.
504 if (flavor_
.isEmpty()) {
505 welcome_ui_
->flavorBanner
->hide();
507 // If needed there are a couple of ways we can make this customizable.
508 // - Add one or more classes, e.g. "note" or "warning" similar to
509 // SyntaxLineEdit, which we can then expose vi #defines.
510 // - Just expose direct color values via #defines.
511 QString flavor_ss
= QString(
513 " border-radius: 0.25em;"
515 " background-color: %2;"
519 .arg("white") // Text color
520 .arg("#2c4bc4"); // Background color. Matches capture start button.
521 // .arg(QColor(tango_butter_5).name()); // "Warning" background
523 welcome_ui_
->flavorBanner
->setText(flavor_
);
524 welcome_ui_
->flavorBanner
->setStyleSheet(flavor_ss
);
526 welcome_ui_
->captureLabel
->setStyleSheet(title_button_ss
);
527 welcome_ui_
->recentLabel
->setStyleSheet(title_button_ss
);
528 welcome_ui_
->helpLabel
->setStyleSheet(title_button_ss
);
530 recent_files_
->setStyleSheet(
531 "QListWidget::item {"
532 " padding-top: 0.2em;"
533 " padding-bottom: 0.2em;"
535 "QListWidget::item::first {"
538 "QListWidget::item::last {"
539 " padding-bottom: 0;"
543 // The helpLinks markup includes its own <style>...</style> section.
544 // Replacing it with a stylesheet and reapplying it like we do above
545 // doesn't work, but this does.
546 QString hl_text
= welcome_ui_
->helpLinks
->text();
547 welcome_ui_
->helpLinks
->clear();
548 welcome_ui_
->helpLinks
->setText(hl_text
);
551 void WelcomePage::on_recentLabel_clicked()
553 mainApp
->doTriggerMenuItem(MainApplication::FileOpenDialog
);