forked from osmocom/wireshark
![Tomasz Moń](/assets/img/avatar_default.png)
Wireshark successfully compiles on Windows with Qt6.2 with following cmake options: -DUSE_qt6=ON -DDISABLE_ERROR=ON QCustomPlot QT 6.2 patch is taken from QCustomPlot forum post by miccs.
638 lines
22 KiB
C++
638 lines
22 KiB
C++
/* about_dialog.cpp
|
|
*
|
|
* Wireshark - Network traffic analyzer
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
* Copyright 1998 Gerald Combs
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "about_dialog.h"
|
|
#include <ui_about_dialog.h>
|
|
|
|
#include "wireshark_application.h"
|
|
#include <wsutil/filesystem.h>
|
|
|
|
#include <QDesktopServices>
|
|
#include <QUrl>
|
|
|
|
#ifdef HAVE_LIBSMI
|
|
#include <epan/oids.h>
|
|
#endif
|
|
|
|
#include <epan/maxmind_db.h>
|
|
|
|
#ifdef HAVE_LUA
|
|
#include <epan/wslua/init_wslua.h>
|
|
#endif
|
|
|
|
#include "ui/alert_box.h"
|
|
#include "ui/last_open_dir.h"
|
|
#include "ui/help_url.h"
|
|
#include <wsutil/utf8_entities.h>
|
|
|
|
#include "file.h"
|
|
#include "wsutil/file_util.h"
|
|
#include "wsutil/tempfile.h"
|
|
#include "wsutil/plugins.h"
|
|
#include "wsutil/copyright_info.h"
|
|
#include "ui/version_info.h"
|
|
|
|
#include "extcap.h"
|
|
|
|
#include <ui/qt/utils/color_utils.h>
|
|
#include <ui/qt/utils/qt_ui_utils.h>
|
|
#include <ui/qt/utils/variant_pointer.h>
|
|
|
|
#include <ui/qt/models/astringlist_list_model.h>
|
|
#include <ui/qt/models/url_link_delegate.h>
|
|
|
|
#include <QFontMetrics>
|
|
#include <QKeySequence>
|
|
#include <QTextStream>
|
|
#include <QUrl>
|
|
#include <QRegularExpression>
|
|
#include <QAbstractItemModel>
|
|
#include <QHash>
|
|
#include <QDesktopServices>
|
|
#include <QClipboard>
|
|
#include <QMenu>
|
|
#include <QFileInfo>
|
|
#include <QMessageBox>
|
|
|
|
AuthorListModel::AuthorListModel(QObject * parent) :
|
|
AStringListListModel(parent)
|
|
{
|
|
bool readAck = false;
|
|
QFile f_authors;
|
|
|
|
f_authors.setFileName(get_datafile_path("AUTHORS-SHORT"));
|
|
f_authors.open(QFile::ReadOnly | QFile::Text);
|
|
QTextStream ReadFile_authors(&f_authors);
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
ReadFile_authors.setEncoding(QStringConverter::Utf8);
|
|
#else
|
|
ReadFile_authors.setCodec("UTF-8");
|
|
#endif
|
|
|
|
QRegularExpression rx("(.*)[<(]([\\s'a-zA-Z0-9._%+-]+(\\[[Aa][Tt]\\])?[a-zA-Z0-9._%+-]+)[>)]");
|
|
acknowledgement_.clear();
|
|
while (!ReadFile_authors.atEnd()) {
|
|
QString line = ReadFile_authors.readLine();
|
|
|
|
if (! readAck && line.trimmed().length() == 0)
|
|
continue;
|
|
if (line.startsWith("------"))
|
|
continue;
|
|
|
|
if (line.contains("Acknowledgements")) {
|
|
readAck = true;
|
|
continue;
|
|
} else {
|
|
QRegularExpressionMatch match = rx.match(line);
|
|
if (match.hasMatch())
|
|
appendRow(QStringList() << match.captured(1).trimmed() << match.captured(2).trimmed());
|
|
}
|
|
|
|
if (readAck && (!line.isEmpty() || !acknowledgement_.isEmpty()))
|
|
acknowledgement_.append(QString("%1\n").arg(line));
|
|
}
|
|
f_authors.close();
|
|
|
|
}
|
|
|
|
AuthorListModel::~AuthorListModel() { }
|
|
|
|
QString AuthorListModel::acknowledgment() const
|
|
{
|
|
return acknowledgement_;
|
|
}
|
|
|
|
QStringList AuthorListModel::headerColumns() const
|
|
{
|
|
return QStringList() << tr("Name") << tr("Email");
|
|
}
|
|
|
|
static void plugins_add_description(const char *name, const char *version,
|
|
const char *types, const char *filename,
|
|
void *user_data)
|
|
{
|
|
QList<QStringList> *plugin_data = (QList<QStringList> *)user_data;
|
|
QStringList plugin_row = QStringList() << name << version << types << filename;
|
|
*plugin_data << plugin_row;
|
|
}
|
|
|
|
PluginListModel::PluginListModel(QObject * parent) : AStringListListModel(parent)
|
|
{
|
|
QList<QStringList> plugin_data;
|
|
#ifdef HAVE_PLUGINS
|
|
plugins_get_descriptions(plugins_add_description, &plugin_data);
|
|
#endif
|
|
|
|
#ifdef HAVE_LUA
|
|
wslua_plugins_get_descriptions(plugins_add_description, &plugin_data);
|
|
#endif
|
|
|
|
extcap_get_descriptions(plugins_add_description, &plugin_data);
|
|
|
|
typeNames_ << QString("");
|
|
foreach(QStringList row, plugin_data)
|
|
{
|
|
QString type_name = row.at(2);
|
|
typeNames_ << type_name;
|
|
appendRow(row);
|
|
}
|
|
|
|
typeNames_.sort();
|
|
typeNames_.removeDuplicates();
|
|
}
|
|
|
|
QStringList PluginListModel::typeNames() const
|
|
{
|
|
return typeNames_;
|
|
}
|
|
|
|
QStringList PluginListModel::headerColumns() const
|
|
{
|
|
return QStringList() << tr("Name") << tr("Version") << tr("Type") << tr("Path");
|
|
}
|
|
|
|
ShortcutListModel::ShortcutListModel(QObject * parent):
|
|
AStringListListModel(parent)
|
|
{
|
|
QMap<QString, QPair<QString, QString> > shortcuts; // name -> (shortcut, description)
|
|
foreach (const QWidget *child, wsApp->mainWindow()->findChildren<QWidget *>()) {
|
|
// Recent items look funny here.
|
|
if (child->objectName().compare("menuOpenRecentCaptureFile") == 0) continue;
|
|
foreach (const QAction *action, child->actions()) {
|
|
|
|
if (!action->shortcut().isEmpty()) {
|
|
QString name = action->text();
|
|
name.replace('&', "");
|
|
shortcuts[name] = QPair<QString, QString>(action->shortcut().toString(QKeySequence::NativeText), action->toolTip());
|
|
}
|
|
}
|
|
}
|
|
|
|
QStringList names = shortcuts.keys();
|
|
names.sort();
|
|
foreach (const QString &name, names) {
|
|
QStringList row;
|
|
row << shortcuts[name].first << name << shortcuts[name].second;
|
|
appendRow(row);
|
|
}
|
|
}
|
|
|
|
QStringList ShortcutListModel::headerColumns() const
|
|
{
|
|
return QStringList() << tr("Shortcut") << tr("Name") << tr("Description");
|
|
}
|
|
|
|
FolderListModel::FolderListModel(QObject * parent):
|
|
AStringListListModel(parent)
|
|
{
|
|
/* "file open" */
|
|
appendRow(QStringList() << tr("\"File\" dialogs") << get_last_open_dir() << tr("capture files"));
|
|
|
|
/* temp */
|
|
appendRow(QStringList() << tr("Temp") << g_get_tmp_dir() << tr("untitled capture files"));
|
|
|
|
/* pers conf */
|
|
appendRow(QStringList() << tr("Personal configuration")
|
|
<< gchar_free_to_qstring(get_persconffile_path("", FALSE))
|
|
<< tr("dfilters, preferences, ethers, …"));
|
|
|
|
/* global conf */
|
|
QString dirPath = get_datafile_dir();
|
|
if (! dirPath.isEmpty()) {
|
|
appendRow (QStringList() << tr("Global configuration") << dirPath
|
|
<< tr("dfilters, preferences, manuf, …"));
|
|
}
|
|
|
|
/* system */
|
|
appendRow(QStringList() << tr("System") << get_systemfile_dir() << tr("ethers, ipxnets"));
|
|
|
|
/* program */
|
|
appendRow(QStringList() << tr("Program") << get_progfile_dir() << tr("program files"));
|
|
|
|
#ifdef HAVE_PLUGINS
|
|
/* pers plugins */
|
|
appendRow(QStringList() << tr("Personal Plugins") << get_plugins_pers_dir_with_version() << tr("binary plugins"));
|
|
|
|
/* global plugins */
|
|
appendRow(QStringList() << tr("Global Plugins") << get_plugins_dir_with_version() << tr("binary plugins"));
|
|
#endif
|
|
|
|
#ifdef HAVE_LUA
|
|
/* pers plugins */
|
|
appendRow(QStringList() << tr("Personal Lua Plugins") << get_plugins_pers_dir() << tr("lua scripts"));
|
|
|
|
/* global plugins */
|
|
appendRow(QStringList() << tr("Global Lua Plugins") << get_plugins_dir() << tr("lua scripts"));
|
|
#endif
|
|
|
|
/* Extcap */
|
|
appendRow(QStringList() << tr("Personal Extcap path") << QString(get_persconffile_path("extcap", FALSE)).trimmed() << tr("Extcap Plugins search path"));
|
|
appendRow(QStringList() << tr("Global Extcap path") << QString(get_extcap_dir()).trimmed() << tr("Extcap Plugins search path"));
|
|
|
|
#ifdef HAVE_MAXMINDDB
|
|
/* MaxMind DB */
|
|
QStringList maxMindDbPaths = QString(maxmind_db_get_paths()).split(G_SEARCHPATH_SEPARATOR_S);
|
|
foreach(QString path, maxMindDbPaths)
|
|
appendRow(QStringList() << tr("MaxMind DB path") << path.trimmed() << tr("MaxMind DB database search path"));
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBSMI
|
|
/* SMI MIBs/PIBs */
|
|
char *default_mib_path = oid_get_default_mib_path();
|
|
QStringList smiPaths = QString(default_mib_path).split(G_SEARCHPATH_SEPARATOR_S);
|
|
g_free(default_mib_path);
|
|
foreach(QString path, smiPaths)
|
|
appendRow(QStringList() << tr("MIB/PIB path") << path.trimmed() << tr("SMI MIB/PIB search path"));
|
|
#endif
|
|
|
|
#ifdef Q_OS_MAC
|
|
/* Mac Extras */
|
|
QString extras_path = wsApp->applicationDirPath() + "/../Resources/Extras";
|
|
appendRow(QStringList() << tr("macOS Extras") << QDir::cleanPath(extras_path) << tr("Extra macOS packages"));
|
|
|
|
#endif
|
|
}
|
|
|
|
QStringList FolderListModel::headerColumns() const
|
|
{
|
|
return QStringList() << tr("Name") << tr("Location") << tr("Typical Files");
|
|
}
|
|
|
|
// To do:
|
|
// - Tweak and enhance ui...
|
|
|
|
AboutDialog::AboutDialog(QWidget *parent) :
|
|
QDialog(parent),
|
|
ui(new Ui::AboutDialog)
|
|
{
|
|
ui->setupUi(this);
|
|
setAttribute(Qt::WA_DeleteOnClose, true);
|
|
QFile f_license;
|
|
|
|
AuthorListModel * authorModel = new AuthorListModel(this);
|
|
AStringListListSortFilterProxyModel * proxyAuthorModel = new AStringListListSortFilterProxyModel(this);
|
|
proxyAuthorModel->setSourceModel(authorModel);
|
|
proxyAuthorModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
|
proxyAuthorModel->setColumnToFilter(0);
|
|
proxyAuthorModel->setColumnToFilter(1);
|
|
ui->tblAuthors->setModel(proxyAuthorModel);
|
|
ui->tblAuthors->setRootIsDecorated(false);
|
|
ui->pte_Authors->clear();
|
|
ui->pte_Authors->appendPlainText(authorModel->acknowledgment());
|
|
ui->pte_Authors->moveCursor(QTextCursor::Start);
|
|
|
|
ui->tblAuthors->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(ui->tblAuthors, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(handleCopyMenu(QPoint)));
|
|
connect(ui->searchAuthors, SIGNAL(textChanged(QString)), proxyAuthorModel, SLOT(setFilter(QString)));
|
|
|
|
/* Wireshark tab */
|
|
updateWiresharkText();
|
|
|
|
ui->pte_wireshark->setFrameStyle(QFrame::NoFrame);
|
|
ui->pte_wireshark->viewport()->setAutoFillBackground(false);
|
|
|
|
/* Check if it is a dev release... (VERSION_MINOR is odd in dev release) */
|
|
#if VERSION_MINOR & 1
|
|
ui->label_logo->setPixmap(QPixmap(":/about/wssplash_dev.png"));
|
|
#endif
|
|
|
|
/* Folders */
|
|
FolderListModel * folderModel = new FolderListModel(this);
|
|
AStringListListSortFilterProxyModel * folderProxyModel = new AStringListListSortFilterProxyModel(this);
|
|
folderProxyModel->setSourceModel(folderModel);
|
|
folderProxyModel->setColumnToFilter(1);
|
|
folderProxyModel->setFilterType(AStringListListSortFilterProxyModel::FilterByStart);
|
|
AStringListListUrlProxyModel * folderDisplayModel = new AStringListListUrlProxyModel(this);
|
|
folderDisplayModel->setSourceModel(folderProxyModel);
|
|
folderDisplayModel->setUrlColumn(1);
|
|
ui->tblFolders->setModel(folderDisplayModel);
|
|
ui->tblFolders->setRootIsDecorated(false);
|
|
ui->tblFolders->setItemDelegateForColumn(1, new UrlLinkDelegate(this));
|
|
ui->tblFolders->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
ui->tblFolders->setTextElideMode(Qt::ElideMiddle);
|
|
ui->tblFolders->setSortingEnabled(true);
|
|
ui->tblFolders->sortByColumn(0, Qt::AscendingOrder);
|
|
connect(ui->tblFolders, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(handleCopyMenu(QPoint)));
|
|
connect(ui->searchFolders, SIGNAL(textChanged(QString)), folderProxyModel, SLOT(setFilter(QString)));
|
|
connect(ui->tblFolders, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(urlDoubleClicked(QModelIndex)));
|
|
|
|
|
|
/* Plugins */
|
|
ui->label_no_plugins->hide();
|
|
PluginListModel * pluginModel = new PluginListModel(this);
|
|
AStringListListSortFilterProxyModel * pluginFilterModel = new AStringListListSortFilterProxyModel(this);
|
|
pluginFilterModel->setSourceModel(pluginModel);
|
|
pluginFilterModel->setColumnToFilter(0);
|
|
AStringListListSortFilterProxyModel * pluginTypeModel = new AStringListListSortFilterProxyModel(this);
|
|
pluginTypeModel->setSourceModel(pluginFilterModel);
|
|
pluginTypeModel->setColumnToFilter(2);
|
|
ui->tblPlugins->setModel(pluginTypeModel);
|
|
ui->tblPlugins->setRootIsDecorated(false);
|
|
UrlLinkDelegate *plugin_delegate = new UrlLinkDelegate(this);
|
|
script_pattern = QString("\\.(lua|py)$");
|
|
plugin_delegate->setColCheck(3, script_pattern);
|
|
ui->tblPlugins->setItemDelegateForColumn(3, plugin_delegate);
|
|
ui->cmbType->addItems(pluginModel->typeNames());
|
|
ui->tblPlugins->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
ui->tblPlugins->setTextElideMode(Qt::ElideMiddle);
|
|
ui->tblPlugins->setSortingEnabled(true);
|
|
ui->tblPlugins->sortByColumn(0, Qt::AscendingOrder);
|
|
connect(ui->tblPlugins, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(handleCopyMenu(QPoint)));
|
|
connect(ui->searchPlugins, SIGNAL(textChanged(QString)), pluginFilterModel, SLOT(setFilter(QString)));
|
|
connect(ui->cmbType, SIGNAL(currentIndexChanged(QString)), pluginTypeModel, SLOT(setFilter(QString)));
|
|
if (ui->tblPlugins->model()->rowCount() < 1) {
|
|
foreach (QWidget *w, ui->tab_plugins->findChildren<QWidget *>()) {
|
|
w->hide();
|
|
}
|
|
ui->label_no_plugins->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
|
|
ui->label_no_plugins->setEnabled(false);
|
|
ui->label_no_plugins->show();
|
|
}
|
|
|
|
/* Shortcuts */
|
|
ShortcutListModel * shortcutModel = new ShortcutListModel(this);
|
|
AStringListListSortFilterProxyModel * shortcutProxyModel = new AStringListListSortFilterProxyModel(this);
|
|
shortcutProxyModel->setSourceModel(shortcutModel);
|
|
shortcutProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
|
shortcutProxyModel->setColumnToFilter(1);
|
|
shortcutProxyModel->setColumnToFilter(2);
|
|
ui->tblShortcuts->setModel(shortcutProxyModel);
|
|
ui->tblShortcuts->setRootIsDecorated(false);
|
|
ui->tblShortcuts->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
ui->tblShortcuts->setSortingEnabled(true);
|
|
ui->tblShortcuts->sortByColumn(1, Qt::AscendingOrder);
|
|
connect(ui->tblShortcuts, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(handleCopyMenu(QPoint)));
|
|
connect(ui->searchShortcuts, SIGNAL(textChanged(QString)), shortcutProxyModel, SLOT(setFilter(QString)));
|
|
|
|
/* License */
|
|
#if defined(_WIN32)
|
|
f_license.setFileName(get_datafile_path("COPYING.txt"));
|
|
#else
|
|
f_license.setFileName(get_datafile_path("COPYING"));
|
|
#endif
|
|
|
|
f_license.open(QFile::ReadOnly | QFile::Text);
|
|
QTextStream ReadFile_license(&f_license);
|
|
|
|
ui->pte_License->insertPlainText(ReadFile_license.readAll());
|
|
ui->pte_License->moveCursor(QTextCursor::Start);
|
|
}
|
|
|
|
AboutDialog::~AboutDialog()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
bool AboutDialog::event(QEvent *event)
|
|
{
|
|
switch (event->type()) {
|
|
case QEvent::ApplicationPaletteChange:
|
|
updateWiresharkText();
|
|
break;
|
|
default:
|
|
break;
|
|
|
|
}
|
|
return QDialog::event(event);
|
|
}
|
|
|
|
void AboutDialog::showEvent(QShowEvent * event)
|
|
{
|
|
int one_em = fontMetrics().height();
|
|
|
|
// Authors: Names slightly narrower than emails.
|
|
QAbstractItemModel *model = ui->tblAuthors->model();
|
|
int column_count = model->columnCount();
|
|
if (column_count) {
|
|
ui->tblAuthors->setColumnWidth(0, (ui->tblAuthors->parentWidget()->width() / column_count) - one_em);
|
|
}
|
|
|
|
// Folders: First and last to contents.
|
|
ui->tblFolders->resizeColumnToContents(0);
|
|
ui->tblFolders->resizeColumnToContents(2);
|
|
ui->tblFolders->setColumnWidth(1, ui->tblFolders->parentWidget()->width() -
|
|
(ui->tblFolders->columnWidth(0) + ui->tblFolders->columnWidth(2)));
|
|
|
|
// Plugins: All but the last to contents.
|
|
model = ui->tblPlugins->model();
|
|
for (int col = 0; model && col < model->columnCount() - 1; col++) {
|
|
ui->tblPlugins->resizeColumnToContents(col);
|
|
}
|
|
|
|
// Contents + 2 em-widths
|
|
ui->tblShortcuts->resizeColumnToContents(0);
|
|
ui->tblShortcuts->setColumnWidth(0, ui->tblShortcuts->columnWidth(0) + (one_em * 2));
|
|
ui->tblShortcuts->setColumnWidth(1, one_em * 12);
|
|
ui->tblShortcuts->resizeColumnToContents(2);
|
|
|
|
QDialog::showEvent(event);
|
|
}
|
|
|
|
void AboutDialog::updateWiresharkText()
|
|
{
|
|
QString vcs_version_info_str = get_ws_vcs_version_info();
|
|
QString copyright_info_str = get_copyright_info();
|
|
QString comp_info_str = gstring_free_to_qbytearray(get_compiled_version_info(get_wireshark_qt_compiled_info,
|
|
get_gui_compiled_info));
|
|
QString runtime_info_str = gstring_free_to_qbytearray(get_runtime_version_info(get_wireshark_runtime_info));
|
|
|
|
QString message = ColorUtils::themeLinkStyle();
|
|
|
|
/* Construct the message string */
|
|
message += "<p>Version " + html_escape(vcs_version_info_str) + "</p>\n\n";
|
|
message += "<p>" + html_escape(copyright_info_str) + "</p>\n\n";
|
|
message += "<p>" + html_escape(comp_info_str) + "</p>\n\n";
|
|
message += "<p>" + html_escape(runtime_info_str) + "</p>\n\n";
|
|
message += "<p>Wireshark is Open Source Software released under the GNU General Public License.</p>\n\n";
|
|
message += "<p>Check the man page and ";
|
|
message += "<a href=https://www.wireshark.org>https://www.wireshark.org</a> ";
|
|
message += "for more information.</p>\n\n";
|
|
ui->pte_wireshark->setHtml(message);
|
|
|
|
/* Save the info for the clipboard copy */
|
|
clipboardInfo = "";
|
|
clipboardInfo += vcs_version_info_str + "\n\n";
|
|
clipboardInfo += gstring_free_to_qbytearray(get_compiled_version_info(get_wireshark_qt_compiled_info,
|
|
get_gui_compiled_info)) + "\n";
|
|
clipboardInfo += gstring_free_to_qbytearray(get_runtime_version_info(get_wireshark_runtime_info));
|
|
}
|
|
|
|
void AboutDialog::on_copyToClipboard_clicked()
|
|
{
|
|
QClipboard * clipBoard = QApplication::clipboard();
|
|
clipBoard->setText(clipboardInfo);
|
|
}
|
|
|
|
void AboutDialog::urlDoubleClicked(const QModelIndex &idx)
|
|
{
|
|
if (idx.column() != 1) {
|
|
return;
|
|
}
|
|
QTreeView * table = qobject_cast<QTreeView *>(sender());
|
|
if (! table)
|
|
return;
|
|
|
|
QString urlText = table->model()->data(idx).toString();
|
|
if (urlText.isEmpty())
|
|
return;
|
|
|
|
if (! QDir(urlText).exists())
|
|
{
|
|
if (QMessageBox::question(this, tr("The directory does not exist"),
|
|
QString(tr("Should the directory %1 be created?").arg(urlText))) == QMessageBox::Yes)
|
|
{
|
|
if (! QDir().mkpath(urlText))
|
|
{
|
|
QMessageBox::warning(this, tr("The directory could not be created"),
|
|
QString(tr("The directory %1 could not be created.").arg(urlText)));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (QDir(urlText).exists())
|
|
{
|
|
QUrl url = QUrl::fromLocalFile(urlText);
|
|
if (url.isValid())
|
|
QDesktopServices::openUrl(url);
|
|
}
|
|
}
|
|
|
|
void AboutDialog::handleCopyMenu(QPoint pos)
|
|
{
|
|
QTreeView * tree = qobject_cast<QTreeView *>(sender());
|
|
if (! tree)
|
|
return;
|
|
|
|
QModelIndex index = tree->indexAt(pos);
|
|
if (! index.isValid())
|
|
return;
|
|
|
|
QMenu * menu = new QMenu(this);
|
|
|
|
if (ui->tabWidget->currentWidget() == ui->tab_plugins)
|
|
{
|
|
#ifdef Q_OS_MAC
|
|
QString show_in_str = tr("Show in Finder");
|
|
#else
|
|
QString show_in_str = tr("Show in Folder");
|
|
#endif
|
|
QAction * showInFolderAction = menu->addAction(show_in_str);
|
|
showInFolderAction->setData(VariantPointer<QTreeView>::asQVariant(tree));
|
|
connect(showInFolderAction, SIGNAL(triggered()), this, SLOT(showInFolderActionTriggered()));
|
|
}
|
|
|
|
QAction * copyColumnAction = menu->addAction(tr("Copy"));
|
|
copyColumnAction->setData(VariantPointer<QTreeView>::asQVariant(tree));
|
|
connect(copyColumnAction, SIGNAL(triggered()), this, SLOT(copyActionTriggered()));
|
|
|
|
QModelIndexList selectedRows = tree->selectionModel()->selectedRows();
|
|
QAction * copyRowAction = menu->addAction(tr("Copy Row(s)", "", selectedRows.count()));
|
|
copyRowAction->setData(VariantPointer<QTreeView>::asQVariant(tree));
|
|
connect(copyRowAction, SIGNAL(triggered()), this, SLOT(copyRowActionTriggered()));
|
|
|
|
menu->popup(tree->viewport()->mapToGlobal(pos));
|
|
}
|
|
|
|
void AboutDialog::showInFolderActionTriggered()
|
|
{
|
|
QAction * sendingAction = qobject_cast<QAction *>(sender());
|
|
if (!sendingAction)
|
|
return;
|
|
|
|
QTreeView * tree = VariantPointer<QTreeView>::asPtr(sendingAction->data());
|
|
QModelIndexList selectedRows = tree->selectionModel()->selectedRows();
|
|
|
|
foreach (QModelIndex index, selectedRows)
|
|
{
|
|
QString cf_path = tree->model()->index(index.row(), 3).data().toString();
|
|
desktop_show_in_folder(cf_path);
|
|
}
|
|
}
|
|
|
|
void AboutDialog::copyRowActionTriggered()
|
|
{
|
|
copyActionTriggered(true);
|
|
}
|
|
|
|
void AboutDialog::copyActionTriggered(bool copyRow)
|
|
{
|
|
QAction * sendingAction = qobject_cast<QAction *>(sender());
|
|
if (! sendingAction)
|
|
return;
|
|
|
|
QTreeView * tree = VariantPointer<QTreeView>::asPtr(sendingAction->data());
|
|
|
|
QModelIndexList selIndeces = tree->selectionModel()->selectedIndexes();
|
|
|
|
int copyColumn = -1;
|
|
if (! copyRow)
|
|
{
|
|
QMenu * menu = qobject_cast<QMenu *>(sendingAction->parentWidget());
|
|
if (menu)
|
|
{
|
|
QPoint menuPosOnTable = tree->mapFromGlobal(menu->pos());
|
|
QModelIndex clickedIndex = tree->indexAt(menuPosOnTable);
|
|
if (clickedIndex.isValid())
|
|
copyColumn = clickedIndex.column();
|
|
}
|
|
}
|
|
|
|
QString clipdata;
|
|
if (selIndeces.count() > 0)
|
|
{
|
|
int columnCount = tree->model()->columnCount();
|
|
QList<int> visitedRows;
|
|
|
|
foreach(QModelIndex index, selIndeces)
|
|
{
|
|
if (visitedRows.contains(index.row()))
|
|
continue;
|
|
|
|
QStringList row;
|
|
if (copyRow)
|
|
{
|
|
for (int cnt = 0; cnt < columnCount; cnt++)
|
|
{
|
|
QModelIndex dataIdx = tree->model()->index(index.row(), cnt);
|
|
row << tree->model()->data(dataIdx).toString();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (copyColumn < 0)
|
|
copyColumn = index.column();
|
|
|
|
QModelIndex dataIdx = tree->model()->index(index.row(), copyColumn);
|
|
row << tree->model()->data(dataIdx).toString();
|
|
}
|
|
|
|
clipdata.append(row.join("\t\t").append("\n"));
|
|
|
|
visitedRows << index.row();
|
|
}
|
|
}
|
|
QClipboard * clipBoard = QApplication::clipboard();
|
|
clipBoard->setText(clipdata);
|
|
}
|
|
|
|
void AboutDialog::on_tblPlugins_doubleClicked(const QModelIndex &index)
|
|
{
|
|
const int path_col = 3;
|
|
if (index.column() != path_col) {
|
|
return;
|
|
}
|
|
const int row = index.row();
|
|
const QAbstractItemModel *model = index.model();
|
|
if (model->index(row, path_col).data().toString().contains(QRegularExpression(script_pattern))) {
|
|
QDesktopServices::openUrl(QUrl::fromLocalFile(model->index(row, path_col).data().toString()));
|
|
}
|
|
}
|