forked from osmocom/wireshark
Qt: Update UI for profiles and handle export/import properly
This patchset ensures a 1:1 replacement of the old 3.0 version of the profiles dialog. It is a major bugfix for the new version in case of handling creating/ deleting and adding profiles. Delete can be performed on multiple profiles now, by selecting the profiles which need to be deleted. Import/Export functionality has been overhauled to follow these rules: * No imports while changes are pending, due to datamodel sanity * Export for Default Profile and Global Profiles is not possible * Either all personal profiles can be selected or individually choosen ones * Use last directory and store it properly * Imports can be cancelled * Only one import is allowed at a time (but it can contain as many profiles as needed) Change-Id: Ie2fccd397202ec06976d764734437284f464409a Reviewed-on: https://code.wireshark.org/review/34123 Petri-Dish: Roland Knall <rknall@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Stig Bjørlykke <stig@bjorlykke.org> Reviewed-by: Roland Knall <rknall@gmail.com>
This commit is contained in:
parent
66747a982b
commit
f259187803
Binary file not shown.
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 118 KiB |
|
@ -784,6 +784,16 @@ are common to all profiles.
|
|||
.The configuration profiles dialog box
|
||||
image::wsug_graphics/ws-gui-config-profiles.png[{medium-screenshot-attrs}]
|
||||
|
||||
Search for profile ...::
|
||||
The list of profiles can be filtered by entering part of the profile's name
|
||||
into the search box.
|
||||
|
||||
Type selection::
|
||||
Profiles can be filtered between displaying "All profiles", "Personal profiles"
|
||||
and "Global profiles"
|
||||
* Personal profiles - these are profiles stored in the user's configuration dirctory
|
||||
* Global profiles - these are profiles provided with Wireshark
|
||||
|
||||
New (+)::
|
||||
Create a new profile. The name of the created profile is “New profile”
|
||||
and is highlighted so that you can more easily change it.
|
||||
|
@ -791,7 +801,9 @@ and is highlighted so that you can more easily change it.
|
|||
Delete (-)::
|
||||
Deletes the selected profile. This includes all configuration files used
|
||||
in this profile. It is not possible to delete the “Default” profile or global
|
||||
profiles.
|
||||
profiles. Multiple profiles can be selected and deleted at the same time, if global
|
||||
profiles are selected they will be skipped, a selected "Default" profile will be
|
||||
resetted.
|
||||
|
||||
Copy::
|
||||
Copies the selected profile. This copies the configuration of the
|
||||
|
@ -799,6 +811,16 @@ profile currently selected in the list. The name of the created profile
|
|||
is the same as the copied profile, with the text “(copy)” and is
|
||||
highlighted so that you can more easily change it.
|
||||
|
||||
btn:[Import]::
|
||||
Profiles can be imported from zip-archives as well as directly from directory
|
||||
structures. Profiles, which already exist by name will be skipped, as well as
|
||||
profiles named "Default".
|
||||
|
||||
btn:[Export]::
|
||||
Profiles can be exported to a zip-archive. Global profiles, as well as the default
|
||||
profile will be skipped during export. Profiles can be selected in the list individually
|
||||
and only the selected profiles will be exported
|
||||
|
||||
btn:[OK]::
|
||||
This button saves all changes, applies the selected profile and closes the
|
||||
dialog.
|
||||
|
|
|
@ -26,6 +26,7 @@ extern "C" {
|
|||
#define PROF_STAT_NEW 3
|
||||
#define PROF_STAT_CHANGED 4
|
||||
#define PROF_STAT_COPY 5
|
||||
#define PROF_STAT_IMPORT 6
|
||||
|
||||
typedef struct {
|
||||
char *name; /* profile name */
|
||||
|
|
|
@ -545,6 +545,7 @@ void MainStatusBar::showProfileMenu(const QPoint &global_pos, Qt::MouseButton bu
|
|||
if ( ! idx.isValid() )
|
||||
continue;
|
||||
|
||||
|
||||
QAction * pa = Q_NULLPTR;
|
||||
QString name = idx.data().toString();
|
||||
if ( idx.data(ProfileModel::DATA_IS_DEFAULT).toBool() )
|
||||
|
@ -590,46 +591,46 @@ void MainStatusBar::showProfileMenu(const QPoint &global_pos, Qt::MouseButton bu
|
|||
|
||||
profile_menu_.setTitle(tr("Switch to"));
|
||||
QMenu ctx_menu_;
|
||||
QAction * action = ctx_menu_.addAction(tr("Manage Profiles" UTF8_HORIZONTAL_ELLIPSIS));
|
||||
QAction * action = ctx_menu_.addAction(tr("Manage Profiles" UTF8_HORIZONTAL_ELLIPSIS), this, SLOT(manageProfile()));
|
||||
action->setProperty("dialog_action_", (int)ProfileDialog::ShowProfiles);
|
||||
connect(action, SIGNAL(triggered()), this, SLOT(manageProfile()));
|
||||
#ifdef HAVE_MINIZIP
|
||||
QMenu * importMenu = new QMenu(tr("Import"));
|
||||
action = importMenu->addAction(tr(UTF8_HORIZONTAL_ELLIPSIS" from Zip"));
|
||||
action->setProperty("dialog_action_", (int)ProfileDialog::ImportZipProfile);
|
||||
connect(action, SIGNAL(triggered()), this, SLOT(manageProfile()));
|
||||
action = importMenu->addAction(tr(UTF8_HORIZONTAL_ELLIPSIS" from Directory"));
|
||||
action->setProperty("dialog_action_", (int)ProfileDialog::ImportDirProfile);
|
||||
connect(action, SIGNAL(triggered()), this, SLOT(manageProfile()));
|
||||
ctx_menu_.addMenu(importMenu);
|
||||
|
||||
QMenu * exportMenu = new QMenu(tr("Export"));
|
||||
action = exportMenu->addAction(tr(UTF8_HORIZONTAL_ELLIPSIS" selected entry"));
|
||||
action->setProperty("dialog_action_", (int)ProfileDialog::ExportSingleProfile);
|
||||
action->setEnabled(enable_edit);
|
||||
connect(action, SIGNAL(triggered()), this, SLOT(manageProfile()));
|
||||
action = exportMenu->addAction(tr(UTF8_HORIZONTAL_ELLIPSIS" all user profiles"));
|
||||
action->setProperty("dialog_action_", (int)ProfileDialog::ExportAllProfiles);
|
||||
connect(action, SIGNAL(triggered()), this, SLOT(manageProfile()));
|
||||
ctx_menu_.addMenu(exportMenu);
|
||||
|
||||
#else
|
||||
action = ctx_menu_.addAction(tr("Import" UTF8_HORIZONTAL_ELLIPSIS));
|
||||
action->setProperty("dialog_action_", (int)ProfileDialog::ImportDirProfile);
|
||||
connect(action, SIGNAL(triggered()), this, SLOT(manageProfile()));
|
||||
#endif
|
||||
ctx_menu_.addSeparator();
|
||||
action = ctx_menu_.addAction(tr("New" UTF8_HORIZONTAL_ELLIPSIS));
|
||||
action = ctx_menu_.addAction(tr("New" UTF8_HORIZONTAL_ELLIPSIS), this, SLOT(manageProfile()));
|
||||
action->setProperty("dialog_action_", (int)ProfileDialog::NewProfile);
|
||||
connect(action, SIGNAL(triggered()), this, SLOT(manageProfile()));
|
||||
action = ctx_menu_.addAction(tr("Edit" UTF8_HORIZONTAL_ELLIPSIS));
|
||||
action = ctx_menu_.addAction(tr("Edit" UTF8_HORIZONTAL_ELLIPSIS), this, SLOT(manageProfile()));
|
||||
action->setProperty("dialog_action_", (int)ProfileDialog::EditCurrentProfile);
|
||||
action->setEnabled(enable_edit);
|
||||
connect(action, SIGNAL(triggered()), this, SLOT(manageProfile()));
|
||||
action = ctx_menu_.addAction(tr("Delete"));
|
||||
action = ctx_menu_.addAction(tr("Delete"), this, SLOT(manageProfile()));
|
||||
action->setProperty("dialog_action_", (int)ProfileDialog::DeleteCurrentProfile);
|
||||
action->setEnabled(enable_edit);
|
||||
connect(action, SIGNAL(triggered()), this, SLOT(manageProfile()));
|
||||
ctx_menu_.addSeparator();
|
||||
|
||||
#ifdef HAVE_MINIZIP
|
||||
QMenu * importMenu = new QMenu(tr("Import"));
|
||||
action = importMenu->addAction(tr("from zip file"), this, SLOT(manageProfile()));
|
||||
action->setProperty("dialog_action_", (int)ProfileDialog::ImportZipProfile);
|
||||
action = importMenu->addAction(tr("from directory"), this, SLOT(manageProfile()));
|
||||
action->setProperty("dialog_action_", (int)ProfileDialog::ImportDirProfile);
|
||||
ctx_menu_.addMenu(importMenu);
|
||||
|
||||
if ( model.userProfilesExist() )
|
||||
{
|
||||
QMenu * exportMenu = new QMenu(tr("Export"));
|
||||
if ( enable_edit )
|
||||
{
|
||||
action = exportMenu->addAction(tr("selected personal profile"), this, SLOT(manageProfile()));
|
||||
action->setProperty("dialog_action_", (int)ProfileDialog::ExportSingleProfile);
|
||||
action->setEnabled(enable_edit);
|
||||
}
|
||||
action = exportMenu->addAction(tr("all personal profiles"), this, SLOT(manageProfile()));
|
||||
action->setProperty("dialog_action_", (int)ProfileDialog::ExportAllProfiles);
|
||||
ctx_menu_.addMenu(exportMenu);
|
||||
}
|
||||
|
||||
#else
|
||||
action = ctx_menu_.addAction(tr("Import"), this, SLOT(manageProfile()));
|
||||
action->setProperty("dialog_action_", (int)ProfileDialog::ImportDirProfile);
|
||||
#endif
|
||||
ctx_menu_.addSeparator();
|
||||
|
||||
ctx_menu_.addMenu(&profile_menu_);
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "glib.h"
|
||||
#include "ui/profile.h"
|
||||
#include "wsutil/filesystem.h"
|
||||
|
@ -80,6 +82,16 @@ void ProfileSortModel::setFilterString(QString txt)
|
|||
invalidateFilter();
|
||||
}
|
||||
|
||||
QStringList ProfileSortModel::filterTypes()
|
||||
{
|
||||
QMap<int, QString> filter_types_;
|
||||
filter_types_.insert(ProfileSortModel::AllProfiles, tr("All profiles"));
|
||||
filter_types_.insert(ProfileSortModel::PersonalProfiles, tr("Personal profiles"));
|
||||
filter_types_.insert(ProfileSortModel::GlobalProfiles, tr("Global profiles"));
|
||||
|
||||
return filter_types_.values();
|
||||
}
|
||||
|
||||
bool ProfileSortModel::filterAcceptsRow(int source_row, const QModelIndex &) const
|
||||
{
|
||||
bool accept = true;
|
||||
|
@ -111,6 +123,9 @@ ProfileModel::ProfileModel(QObject * parent) :
|
|||
set_profile_ = get_profile_name();
|
||||
|
||||
reset_default_ = false;
|
||||
profiles_imported_ = false;
|
||||
|
||||
last_set_row_ = 0;
|
||||
|
||||
loadProfiles();
|
||||
}
|
||||
|
@ -183,6 +198,24 @@ bool ProfileModel::changesPending() const
|
|||
return pending;
|
||||
}
|
||||
|
||||
bool ProfileModel::importPending() const
|
||||
{
|
||||
return profiles_imported_;
|
||||
}
|
||||
|
||||
bool ProfileModel::userProfilesExist() const
|
||||
{
|
||||
bool user_exists = false;
|
||||
for ( int cnt = 0; cnt < rowCount() && ! user_exists; cnt++ )
|
||||
{
|
||||
QModelIndex idx = index(cnt, ProfileModel::COL_NAME);
|
||||
if ( ! idx.data(ProfileModel::DATA_IS_GLOBAL).toBool() && ! idx.data(ProfileModel::DATA_IS_DEFAULT).toBool() )
|
||||
user_exists = true;
|
||||
}
|
||||
|
||||
return user_exists;
|
||||
}
|
||||
|
||||
int ProfileModel::rowCount(const QModelIndex &) const
|
||||
{
|
||||
return profiles_.count();
|
||||
|
@ -204,7 +237,7 @@ profile_def * ProfileModel::guard(int row) const
|
|||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
return profiles_.at(row);
|
||||
return profiles_.value(row, Q_NULLPTR);
|
||||
}
|
||||
|
||||
QVariant ProfileModel::dataDisplay(const QModelIndex &index) const
|
||||
|
@ -312,6 +345,8 @@ QVariant ProfileModel::dataPath(const QModelIndex &index) const
|
|||
return gchar_free_to_qstring(get_persconffile_path("", FALSE));
|
||||
else
|
||||
return tr("Resetting to default");
|
||||
case PROF_STAT_IMPORT:
|
||||
return tr("Imported profile");
|
||||
case PROF_STAT_EXISTS:
|
||||
{
|
||||
QString profile_path;
|
||||
|
@ -336,23 +371,39 @@ QVariant ProfileModel::dataPath(const QModelIndex &index) const
|
|||
return tr("Created from default settings");
|
||||
}
|
||||
case PROF_STAT_CHANGED:
|
||||
if (prof->reference)
|
||||
return QString("%1 %2").arg(tr("Renamed from: ")).arg(prof->reference);
|
||||
break;
|
||||
{
|
||||
QString msg;
|
||||
if ( ! ProfileModel::checkNameValidity(QString(prof->name), &msg) )
|
||||
return msg;
|
||||
|
||||
if (prof->reference)
|
||||
return QString("%1 %2").arg(tr("Renamed from: ")).arg(prof->reference);
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
case PROF_STAT_COPY:
|
||||
if (prof->reference)
|
||||
{
|
||||
ProfileModel * nthis = const_cast<ProfileModel *>(this);
|
||||
int row = nthis->findByNameAndVisibility(prof->reference, false, true);
|
||||
profile_def * ref = Q_NULLPTR;
|
||||
if ( row > 0 && row != index.row() )
|
||||
ref = guard(row);
|
||||
else
|
||||
row = -1;
|
||||
|
||||
/* Security blanket. It should not happen with PROF_STAT_COPY, but just in case */
|
||||
if ( ! prof->reference )
|
||||
return tr("Created from default settings");
|
||||
|
||||
QString msg = QString("%1 %2").arg(tr("Copied from: ")).arg(prof->reference);
|
||||
|
||||
if ( profile_exists(prof->reference, TRUE) && prof->from_global )
|
||||
msg.append(QString(" (%1)").arg(tr("system provided")));
|
||||
else
|
||||
{
|
||||
ProfileModel * nthis = const_cast<ProfileModel *>(this);
|
||||
int row = nthis->findByNameAndVisibility(prof->reference);
|
||||
if ( row < 0 )
|
||||
msg.append(QString(" (%1)").arg(tr("deleted")));
|
||||
}
|
||||
else if ( row > 0 && ref && QString(ref->name).compare(prof->reference) != 0 )
|
||||
msg.append(QString(" (%1 %2)").arg(tr("renamed to")).arg(ref->name));
|
||||
else if ( row < 0 )
|
||||
msg.append(QString(" (%1)").arg(tr("deleted")));
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
@ -377,16 +428,12 @@ QVariant ProfileModel::data(const QModelIndex &index, int role) const
|
|||
{
|
||||
case Qt::DisplayRole:
|
||||
return dataDisplay(index);
|
||||
break;
|
||||
case Qt::FontRole:
|
||||
return dataFontRole(index);
|
||||
break;
|
||||
case Qt::BackgroundColorRole:
|
||||
return dataBackgroundRole(index);
|
||||
break;
|
||||
case Qt::ToolTipRole:
|
||||
return dataToolTipRole(index);
|
||||
break;
|
||||
case ProfileModel::DATA_STATUS:
|
||||
return qVariantFromValue(prof->status);
|
||||
case ProfileModel::DATA_IS_DEFAULT:
|
||||
|
@ -407,15 +454,16 @@ QVariant ProfileModel::data(const QModelIndex &index, int role) const
|
|||
}
|
||||
return qVariantFromValue(false);
|
||||
}
|
||||
break;
|
||||
case ProfileModel::DATA_PATH:
|
||||
return dataPath(index);
|
||||
break;
|
||||
|
||||
case ProfileModel::DATA_INDEX_VALUE_IS_URL:
|
||||
if ( index.column() <= ProfileModel::COL_TYPE )
|
||||
return qVariantFromValue(false);
|
||||
return qVariantFromValue(true);
|
||||
case ProfileModel::DATA_PATH_IS_NOT_DESCRIPTION:
|
||||
if ( prof->status == PROF_STAT_NEW || prof->status == PROF_STAT_COPY
|
||||
|| ( prof->status == PROF_STAT_DEFAULT && reset_default_ )
|
||||
|| prof->status == PROF_STAT_CHANGED )
|
||||
|| prof->status == PROF_STAT_CHANGED || prof->status == PROF_STAT_IMPORT )
|
||||
return qVariantFromValue(false);
|
||||
else
|
||||
return qVariantFromValue(true);
|
||||
|
@ -471,21 +519,24 @@ int ProfileModel::findByName(QString name)
|
|||
return row;
|
||||
}
|
||||
|
||||
int ProfileModel::findByNameAndVisibility(QString name, bool isGlobal)
|
||||
int ProfileModel::findByNameAndVisibility(QString name, bool isGlobal, bool searchReference)
|
||||
{
|
||||
QList<int> result = findAllByNameAndVisibility(name, isGlobal);
|
||||
QList<int> result = findAllByNameAndVisibility(name, isGlobal, searchReference);
|
||||
return result.count() == 0 ? -1 : result.at(0);
|
||||
}
|
||||
|
||||
QList<int> ProfileModel::findAllByNameAndVisibility(QString name, bool isGlobal)
|
||||
QList<int> ProfileModel::findAllByNameAndVisibility(QString name, bool isGlobal, bool searchReference)
|
||||
{
|
||||
QList<int> result;
|
||||
|
||||
for ( int cnt = 0; cnt < profiles_.count(); cnt++ )
|
||||
{
|
||||
profile_def * prof = guard(cnt);
|
||||
if ( prof && static_cast<bool>(prof->is_global) == isGlobal && name.compare(prof->name) == 0 )
|
||||
result << cnt;
|
||||
if ( prof && static_cast<bool>(prof->is_global) == isGlobal )
|
||||
{
|
||||
if ( name.compare(prof->name) == 0 || ( searchReference && name.compare(prof->reference) == 0 ) )
|
||||
result << cnt;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -508,7 +559,7 @@ QModelIndex ProfileModel::addNewProfile(QString name)
|
|||
return index(findByName(newName), COL_NAME);
|
||||
}
|
||||
|
||||
QModelIndex ProfileModel::duplicateEntry(QModelIndex idx)
|
||||
QModelIndex ProfileModel::duplicateEntry(QModelIndex idx, int new_status)
|
||||
{
|
||||
if ( ! idx.isValid() )
|
||||
return QModelIndex();
|
||||
|
@ -517,17 +568,42 @@ QModelIndex ProfileModel::duplicateEntry(QModelIndex idx)
|
|||
if ( ! prof )
|
||||
return QModelIndex();
|
||||
|
||||
if ( new_status < 0 || new_status > PROF_STAT_COPY )
|
||||
new_status = PROF_STAT_COPY;
|
||||
|
||||
if ( prof->status == PROF_STAT_COPY && ! prof->from_global )
|
||||
{
|
||||
int row = findByNameAndVisibility(prof->reference, false);
|
||||
profile_def * copyParent = guard(row);
|
||||
if ( copyParent && copyParent->status == PROF_STAT_NEW )
|
||||
return duplicateEntry(index(row, ProfileModel::COL_NAME), PROF_STAT_NEW);
|
||||
}
|
||||
|
||||
QString parent = prof->name;
|
||||
if ( ! prof->is_global && prof->status != PROF_STAT_CHANGED && prof->status != PROF_STAT_NEW )
|
||||
if ( prof->status == PROF_STAT_NEW )
|
||||
{
|
||||
if ( QString(prof->reference).length() > 0 )
|
||||
parent = QString(prof->reference);
|
||||
}
|
||||
else if ( ! prof->is_global && prof->status != PROF_STAT_CHANGED )
|
||||
parent = get_profile_parent (prof->name);
|
||||
else if ( prof->status == PROF_STAT_CHANGED )
|
||||
parent = prof->reference;
|
||||
|
||||
QString parentName = parent;
|
||||
if ( prof->status == PROF_STAT_CHANGED )
|
||||
parentName = prof->name;
|
||||
|
||||
if ( parent.length() == 0 )
|
||||
return QModelIndex();
|
||||
|
||||
QString new_name;
|
||||
if (prof->is_global && ! profile_exists (parent.toUtf8().constData(), FALSE))
|
||||
new_name = QString(prof->name);
|
||||
else
|
||||
new_name = QString("%1 (%2)").arg(parent).arg(tr("copy", "noun"));
|
||||
new_name = QString("%1 (%2)").arg(parentName).arg(tr("copy", "noun"));
|
||||
|
||||
if ( findByNameAndVisibility(new_name) >= 0 )
|
||||
if ( findByNameAndVisibility(new_name) >= 0 && new_name.length() > 0 )
|
||||
{
|
||||
int cnt = 1;
|
||||
QString copyName = new_name;
|
||||
|
@ -543,7 +619,10 @@ QModelIndex ProfileModel::duplicateEntry(QModelIndex idx)
|
|||
if ( new_name.compare(QString(new_name.toUtf8().constData())) != 0 && !prof->is_global )
|
||||
return QModelIndex();
|
||||
|
||||
add_to_profile_list(new_name.toUtf8().constData(), parent.toUtf8().constData(), PROF_STAT_COPY, FALSE, prof->from_global);
|
||||
if ( new_status == PROF_STAT_COPY && prof->status == PROF_STAT_NEW )
|
||||
new_status = PROF_STAT_NEW;
|
||||
|
||||
add_to_profile_list(new_name.toUtf8().constData(), parent.toUtf8().constData(), new_status, FALSE, prof->from_global);
|
||||
loadProfiles();
|
||||
|
||||
int row = findByNameAndVisibility(new_name, false);
|
||||
|
@ -558,29 +637,57 @@ void ProfileModel::deleteEntry(QModelIndex idx)
|
|||
if ( ! idx.isValid() )
|
||||
return;
|
||||
|
||||
profile_def * prof = guard(idx.row());
|
||||
if ( ! prof )
|
||||
return;
|
||||
QModelIndexList temp;
|
||||
temp << idx;
|
||||
deleteEntries(temp);
|
||||
}
|
||||
|
||||
if ( prof->is_global )
|
||||
return;
|
||||
void ProfileModel::deleteEntries(QModelIndexList idcs)
|
||||
{
|
||||
bool changes = false;
|
||||
|
||||
if ( prof->status == PROF_STAT_DEFAULT )
|
||||
QList<int> indeces;
|
||||
foreach ( QModelIndex idx, idcs )
|
||||
{
|
||||
if ( ! indeces.contains(idx.row()) && ! idx.data(ProfileModel::DATA_IS_GLOBAL).toBool() )
|
||||
indeces << idx.row();
|
||||
}
|
||||
/* Security blanket. This ensures, that we start deleting from the end and do not get any issues iterating the list */
|
||||
std::sort(indeces.begin(), indeces.end(), std::less<int>());
|
||||
|
||||
foreach ( int row, indeces )
|
||||
{
|
||||
profile_def * prof = guard(row);
|
||||
if ( ! prof )
|
||||
continue;
|
||||
|
||||
if ( prof->is_global )
|
||||
continue;
|
||||
|
||||
if ( prof->status == PROF_STAT_DEFAULT )
|
||||
{
|
||||
reset_default_ = ! reset_default_;
|
||||
}
|
||||
else
|
||||
{
|
||||
GList * fl_entry = entry(prof);
|
||||
if ( fl_entry )
|
||||
{
|
||||
changes = true;
|
||||
remove_from_profile_list(fl_entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( changes )
|
||||
loadProfiles();
|
||||
|
||||
if ( reset_default_ )
|
||||
{
|
||||
emit layoutAboutToBeChanged();
|
||||
reset_default_ = ! reset_default_;
|
||||
emit dataChanged(index(0, 0), index(rowCount(), columnCount()));
|
||||
emit layoutChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
GList * fl_entry = entry(prof);
|
||||
if ( fl_entry )
|
||||
{
|
||||
remove_from_profile_list(fl_entry);
|
||||
loadProfiles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ProfileModel::resetDefault() const
|
||||
|
@ -588,9 +695,12 @@ bool ProfileModel::resetDefault() const
|
|||
return reset_default_;
|
||||
}
|
||||
|
||||
void ProfileModel::doResetModel()
|
||||
void ProfileModel::doResetModel(bool reset_import)
|
||||
{
|
||||
reset_default_ = false;
|
||||
if ( reset_import )
|
||||
profiles_imported_ = false;
|
||||
|
||||
loadProfiles();
|
||||
}
|
||||
|
||||
|
@ -613,6 +723,8 @@ QModelIndex ProfileModel::activeProfile() const
|
|||
|
||||
bool ProfileModel::setData(const QModelIndex &idx, const QVariant &value, int role)
|
||||
{
|
||||
last_set_row_ = -1;
|
||||
|
||||
if ( role != Qt::EditRole || ! idx.isValid() )
|
||||
return false;
|
||||
|
||||
|
@ -623,6 +735,8 @@ bool ProfileModel::setData(const QModelIndex &idx, const QVariant &value, int ro
|
|||
if ( ! prof || prof->status == PROF_STAT_DEFAULT )
|
||||
return false;
|
||||
|
||||
last_set_row_ = idx.row();
|
||||
|
||||
QString current(prof->name);
|
||||
if ( current.compare(value.toString()) != 0 )
|
||||
{
|
||||
|
@ -634,6 +748,7 @@ bool ProfileModel::setData(const QModelIndex &idx, const QVariant &value, int ro
|
|||
} else if (prof->status == PROF_STAT_EXISTS) {
|
||||
prof->status = PROF_STAT_CHANGED;
|
||||
}
|
||||
emit itemChanged(idx);
|
||||
}
|
||||
|
||||
loadProfiles();
|
||||
|
@ -641,6 +756,11 @@ bool ProfileModel::setData(const QModelIndex &idx, const QVariant &value, int ro
|
|||
return true;
|
||||
}
|
||||
|
||||
int ProfileModel::lastSetRow() const
|
||||
{
|
||||
return last_set_row_;
|
||||
}
|
||||
|
||||
bool ProfileModel::copyTempToProfile(QString tempPath, QString profilePath)
|
||||
{
|
||||
QDir profileDir(profilePath);
|
||||
|
@ -672,10 +792,29 @@ bool ProfileModel::copyTempToProfile(QString tempPath, QString profilePath)
|
|||
return false;
|
||||
}
|
||||
|
||||
QFileInfoList ProfileModel::uniquePaths(QFileInfoList lst)
|
||||
{
|
||||
QStringList files;
|
||||
QFileInfoList newLst;
|
||||
|
||||
foreach ( QFileInfo entry, lst )
|
||||
{
|
||||
if ( ! files.contains(entry.absoluteFilePath()) )
|
||||
{
|
||||
if ( entry.exists() && entry.isDir() )
|
||||
{
|
||||
newLst << entry.absoluteFilePath();
|
||||
files << entry.absoluteFilePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newLst;
|
||||
}
|
||||
|
||||
QFileInfoList ProfileModel::filterProfilePath(QString path, QFileInfoList ent, bool fromZip)
|
||||
{
|
||||
QFileInfoList result = ent;
|
||||
|
||||
QDir temp(path);
|
||||
temp.setSorting(QDir::Name);
|
||||
temp.setFilter(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot);
|
||||
|
@ -696,9 +835,14 @@ QFileInfoList ProfileModel::filterProfilePath(QString path, QFileInfoList ent, b
|
|||
}
|
||||
|
||||
if ( found )
|
||||
{
|
||||
result.append(entry);
|
||||
}
|
||||
else
|
||||
result.append(filterProfilePath(entry.absoluteFilePath(), result, fromZip));
|
||||
{
|
||||
if ( path.compare(entry.absoluteFilePath()) != 0 )
|
||||
result.append(filterProfilePath(entry.absoluteFilePath(), result, fromZip));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -756,36 +900,35 @@ bool ProfileModel::exportProfiles(QString filename, QModelIndexList items, QStri
|
|||
/* This check runs BEFORE the file has been unzipped! */
|
||||
bool ProfileModel::acceptFile(QString fileName, int fileSize)
|
||||
{
|
||||
if ( fileName.contains(".") || fileName.startsWith("_") )
|
||||
if ( fileName.toLower().endsWith(".zip") )
|
||||
return false;
|
||||
|
||||
if ( fileSize > 1024 * 512 )
|
||||
return false;
|
||||
|
||||
/* config_file_exists_with_entries cannot be used, due to the fact, that the file has not been extracted yet */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int ProfileModel::importProfilesFromZip(QString filename, int * skippedCnt)
|
||||
int ProfileModel::importProfilesFromZip(QString filename, int * skippedCnt, QStringList *result)
|
||||
{
|
||||
QTemporaryDir dir;
|
||||
#if 0
|
||||
dir.setAutoRemove(false);
|
||||
g_printerr("Temp dir for unzip: %s\n", dir.path().toUtf8().constData());
|
||||
#endif
|
||||
|
||||
int cnt = 0;
|
||||
if ( dir.isValid() )
|
||||
{
|
||||
WireSharkZipHelper::unzip(filename, dir.path(), &ProfileModel::acceptFile);
|
||||
cnt = importProfilesFromDir(dir.path(), skippedCnt, true);
|
||||
cnt = importProfilesFromDir(dir.path(), skippedCnt, true, result);
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
#endif
|
||||
|
||||
int ProfileModel::importProfilesFromDir(QString dirname, int * skippedCnt, bool fromZip)
|
||||
int ProfileModel::importProfilesFromDir(QString dirname, int * skippedCnt, bool fromZip, QStringList *result)
|
||||
{
|
||||
int count = 0;
|
||||
int skipped = 0;
|
||||
|
@ -793,7 +936,8 @@ int ProfileModel::importProfilesFromDir(QString dirname, int * skippedCnt, bool
|
|||
QDir dir(dirname);
|
||||
if ( dir.exists() )
|
||||
{
|
||||
QFileInfoList entries = filterProfilePath(dirname, QFileInfoList(), fromZip);
|
||||
QFileInfoList entries = uniquePaths(filterProfilePath(dirname, QFileInfoList(), fromZip));
|
||||
*skippedCnt = 0;
|
||||
|
||||
int entryCount = 0;
|
||||
foreach ( QFileInfo fentry, entries )
|
||||
|
@ -809,6 +953,9 @@ int ProfileModel::importProfilesFromDir(QString dirname, int * skippedCnt, bool
|
|||
continue;
|
||||
}
|
||||
|
||||
if ( result )
|
||||
*result << fentry.fileName();
|
||||
|
||||
if ( copyTempToProfile(tempPath, profilePath) )
|
||||
{
|
||||
count++;
|
||||
|
@ -818,7 +965,10 @@ int ProfileModel::importProfilesFromDir(QString dirname, int * skippedCnt, bool
|
|||
}
|
||||
|
||||
if ( count > 0 )
|
||||
{
|
||||
profiles_imported_ = true;
|
||||
loadProfiles();
|
||||
}
|
||||
|
||||
if ( skippedCnt )
|
||||
*skippedCnt = skipped;
|
||||
|
@ -826,6 +976,57 @@ int ProfileModel::importProfilesFromDir(QString dirname, int * skippedCnt, bool
|
|||
return count;
|
||||
}
|
||||
|
||||
void ProfileModel::markAsImported(QStringList importedItems)
|
||||
{
|
||||
if ( importedItems.count() <= 0 )
|
||||
return;
|
||||
|
||||
profiles_imported_ = true;
|
||||
|
||||
foreach ( QString item, importedItems )
|
||||
{
|
||||
int row = findByNameAndVisibility(item, false);
|
||||
profile_def * prof = guard(row);
|
||||
if ( ! prof )
|
||||
continue;
|
||||
|
||||
prof->status = PROF_STAT_IMPORT;
|
||||
prof->from_global = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool ProfileModel::clearImported(QString *msg)
|
||||
{
|
||||
QList<int> rows;
|
||||
bool result = true;
|
||||
for ( int cnt = 0; cnt < rowCount(); cnt++ )
|
||||
{
|
||||
profile_def * prof = guard(cnt);
|
||||
if ( prof && prof->status == PROF_STAT_IMPORT && ! rows.contains(cnt) )
|
||||
rows << cnt;
|
||||
}
|
||||
/* Security blanket. This ensures, that we start deleting from the end and do not get any issues iterating the list */
|
||||
std::sort(rows.begin(), rows.end(), std::less<int>());
|
||||
|
||||
char * ret_path = Q_NULLPTR;
|
||||
for ( int cnt = 0; cnt < rows.count() && result; cnt++ )
|
||||
{
|
||||
int row = rows.at(cnt);
|
||||
if ( delete_persconffile_profile ( index(row, ProfileModel::COL_NAME).data().toString().toUtf8().constData(), &ret_path ) != 0 )
|
||||
{
|
||||
if ( msg )
|
||||
{
|
||||
QString errmsg = QString("%1\n\"%2\":\n%3.").arg(tr("Can't delete profile directory")).arg(ret_path).arg(g_strerror(errno));
|
||||
msg->append(errmsg);
|
||||
}
|
||||
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ProfileModel::checkNameValidity(QString name, QString *msg)
|
||||
{
|
||||
QString message;
|
||||
|
@ -845,17 +1046,18 @@ bool ProfileModel::checkNameValidity(QString name, QString *msg)
|
|||
if ( name.contains(invalid_dir_chars[cnt]) )
|
||||
invalid = true;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if ( invalid )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
message = tr("A profile name cannot contain the following characters: %1").arg(msgChars);
|
||||
#else
|
||||
message = tr("A profile name cannot contain the '/' character.");
|
||||
#endif
|
||||
}
|
||||
|
||||
if ( message.isEmpty() && ( name.startsWith('.') || name.endsWith('.') ) )
|
||||
message = tr("A profile cannot start or end with a period (.)");
|
||||
#else
|
||||
if ( invalid )
|
||||
message = tr("A profile name cannot contain the '/' character.");
|
||||
#endif
|
||||
|
||||
if (! message.isEmpty()) {
|
||||
if (msg)
|
||||
|
|
|
@ -30,13 +30,15 @@ public:
|
|||
|
||||
enum FilterType {
|
||||
AllProfiles = 0,
|
||||
GlobalProfiles,
|
||||
PersonalProfiles
|
||||
PersonalProfiles,
|
||||
GlobalProfiles
|
||||
};
|
||||
|
||||
void setFilterType(FilterType ft);
|
||||
void setFilterString(QString txt = QString());
|
||||
|
||||
static QStringList filterTypes();
|
||||
|
||||
protected:
|
||||
virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const;
|
||||
virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
|
||||
|
@ -44,7 +46,6 @@ protected:
|
|||
private:
|
||||
FilterType ft_;
|
||||
QString ftext_;
|
||||
|
||||
};
|
||||
|
||||
class ProfileModel : public QAbstractTableModel
|
||||
|
@ -66,7 +67,8 @@ public:
|
|||
DATA_IS_GLOBAL,
|
||||
DATA_IS_SELECTED,
|
||||
DATA_PATH,
|
||||
DATA_PATH_IS_NOT_DESCRIPTION
|
||||
DATA_PATH_IS_NOT_DESCRIPTION,
|
||||
DATA_INDEX_VALUE_IS_URL
|
||||
} data_values_;
|
||||
|
||||
// QAbstractItemModel interface
|
||||
|
@ -78,12 +80,13 @@ public:
|
|||
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||
|
||||
void deleteEntry(QModelIndex idx);
|
||||
void deleteEntries(QModelIndexList idcs);
|
||||
|
||||
int findByName(QString name);
|
||||
QModelIndex addNewProfile(QString name);
|
||||
QModelIndex duplicateEntry(QModelIndex idx);
|
||||
QModelIndex duplicateEntry(QModelIndex idx, int new_status = PROF_STAT_COPY);
|
||||
|
||||
void doResetModel();
|
||||
void doResetModel(bool reset_import = false);
|
||||
bool resetDefault() const;
|
||||
|
||||
QModelIndex activeProfile() const;
|
||||
|
@ -91,29 +94,39 @@ public:
|
|||
GList * at(int row) const;
|
||||
|
||||
bool changesPending() const;
|
||||
bool importPending() const;
|
||||
|
||||
bool userProfilesExist() const;
|
||||
|
||||
#ifdef HAVE_MINIZIP
|
||||
QStringList exportFileList(QModelIndexList items);
|
||||
bool exportProfiles(QString filename, QModelIndexList items, QString * err = Q_NULLPTR);
|
||||
int importProfilesFromZip(QString filename, int *skippedCnt = Q_NULLPTR);
|
||||
int importProfilesFromZip(QString filename, int *skippedCnt = Q_NULLPTR, QStringList *result = Q_NULLPTR);
|
||||
#endif
|
||||
int importProfilesFromDir(QString filename, int *skippedCnt = Q_NULLPTR, bool fromZip = false);
|
||||
bool copyTempToProfile(QString tempPath, QString profilePath);
|
||||
QFileInfoList filterProfilePath(QString, QFileInfoList ent, bool fromZip);
|
||||
int importProfilesFromDir(QString filename, int *skippedCnt = Q_NULLPTR, bool fromZip = false, QStringList *result = Q_NULLPTR);
|
||||
|
||||
static bool checkNameValidity(QString name, QString *msg = Q_NULLPTR);
|
||||
QList<int> findAllByNameAndVisibility(QString name, bool isGlobal = false);
|
||||
QList<int> findAllByNameAndVisibility(QString name, bool isGlobal = false, bool searchReference = false);
|
||||
void markAsImported(QStringList importedItems);
|
||||
bool clearImported(QString *msg = Q_NULLPTR);
|
||||
|
||||
int lastSetRow() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void itemChanged(const QModelIndex &idx);
|
||||
|
||||
private:
|
||||
QList<profile_def *> profiles_;
|
||||
QString set_profile_;
|
||||
bool reset_default_;
|
||||
bool profiles_imported_;
|
||||
|
||||
int last_set_row_;
|
||||
|
||||
void loadProfiles();
|
||||
profile_def * guard(int row) const;
|
||||
GList * entry(profile_def *) const;
|
||||
|
||||
int findByNameAndVisibility(QString name, bool isGlobal = false);
|
||||
int findByNameAndVisibility(QString name, bool isGlobal = false, bool searchReference = false);
|
||||
|
||||
#ifdef HAVE_MINIZIP
|
||||
static bool acceptFile(QString fileName, int fileSize);
|
||||
|
@ -125,6 +138,13 @@ private:
|
|||
QVariant dataToolTipRole(const QModelIndex & idx) const;
|
||||
QVariant dataPath(const QModelIndex & idx) const;
|
||||
|
||||
#ifdef HAVE_MINIZIP
|
||||
QStringList exportFileList(QModelIndexList items);
|
||||
#endif
|
||||
bool copyTempToProfile(QString tempPath, QString profilePath);
|
||||
QFileInfoList filterProfilePath(QString, QFileInfoList ent, bool fromZip);
|
||||
QFileInfoList uniquePaths(QFileInfoList lst);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include "ui/profile.h"
|
||||
#include "ui/recent.h"
|
||||
#include "ui/last_open_dir.h"
|
||||
|
||||
#include <ui/qt/utils/variant_pointer.h>
|
||||
#include <ui/qt/models/profile_model.h>
|
||||
|
@ -80,17 +81,17 @@ ProfileDialog::ProfileDialog(QWidget *parent) :
|
|||
export_button_ = pd_ui_->buttonBox->addButton(tr("Export", "noun"), QDialogButtonBox::ActionRole);
|
||||
|
||||
QMenu * importMenu = new QMenu(import_button_);
|
||||
QAction * entry = importMenu->addAction(tr(UTF8_HORIZONTAL_ELLIPSIS " from Zip"));
|
||||
QAction * entry = importMenu->addAction(tr("from zip file"));
|
||||
connect( entry, &QAction::triggered, this, &ProfileDialog::importFromZip);
|
||||
entry = importMenu->addAction(tr(UTF8_HORIZONTAL_ELLIPSIS " from Directory"));
|
||||
entry = importMenu->addAction(tr("from directory"));
|
||||
connect( entry, &QAction::triggered, this, &ProfileDialog::importFromDirectory);
|
||||
import_button_->setMenu(importMenu);
|
||||
|
||||
QMenu * exportMenu = new QMenu(export_button_);
|
||||
export_selected_entry_ = exportMenu->addAction(tr(UTF8_HORIZONTAL_ELLIPSIS " selected entry"));
|
||||
export_selected_entry_ = exportMenu->addAction(tr("%Ln selected personal profile(s)", "", 0));
|
||||
export_selected_entry_->setProperty(PROFILE_EXPORT_PROPERTY, PROFILE_EXPORT_SELECTED);
|
||||
connect( export_selected_entry_, &QAction::triggered, this, &ProfileDialog::exportProfiles);
|
||||
entry = exportMenu->addAction(tr(UTF8_HORIZONTAL_ELLIPSIS " all personal profiles"));
|
||||
entry = exportMenu->addAction(tr("all personal profiles"));
|
||||
entry->setProperty(PROFILE_EXPORT_PROPERTY, PROFILE_EXPORT_ALL);
|
||||
connect( entry, &QAction::triggered, this, &ProfileDialog::exportProfiles);
|
||||
export_button_->setMenu(exportMenu);
|
||||
|
@ -100,18 +101,10 @@ ProfileDialog::ProfileDialog(QWidget *parent) :
|
|||
|
||||
resetTreeView();
|
||||
|
||||
connect(pd_ui_->profileTreeView, &ProfileTreeView::currentItemChanged,
|
||||
this, &ProfileDialog::currentItemChanged);
|
||||
|
||||
connect(pd_ui_->profileTreeView, &ProfileTreeView::itemUpdated,
|
||||
this, &ProfileDialog::editingFinished);
|
||||
|
||||
/* Select the row for the currently selected profile or the first row if non is selected*/
|
||||
selectProfile();
|
||||
|
||||
QStringList items;
|
||||
items << tr("All profiles") << tr("Personal profiles") << tr("Global profiles");
|
||||
pd_ui_->cmbProfileTypes->addItems(items);
|
||||
pd_ui_->cmbProfileTypes->addItems(ProfileSortModel::filterTypes());
|
||||
|
||||
connect (pd_ui_->cmbProfileTypes, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(filterChanged(const QString &)));
|
||||
|
@ -194,35 +187,150 @@ int ProfileDialog::execAction(ProfileDialog::ProfileAction profile_action)
|
|||
return ret;
|
||||
}
|
||||
|
||||
QModelIndexList ProfileDialog::selectedProfiles()
|
||||
{
|
||||
QModelIndexList profiles;
|
||||
|
||||
foreach (QModelIndex idx, pd_ui_->profileTreeView->selectionModel()->selectedIndexes())
|
||||
{
|
||||
QModelIndex temp = sort_model_->mapToSource(idx);
|
||||
if ( ! temp.isValid() || profiles.contains(temp) || temp.column() != ProfileModel::COL_NAME )
|
||||
continue;
|
||||
|
||||
profiles << temp;
|
||||
}
|
||||
|
||||
return profiles;
|
||||
}
|
||||
|
||||
void ProfileDialog::selectionChanged()
|
||||
{
|
||||
if ( selectedProfiles().count() == 0 )
|
||||
pd_ui_->profileTreeView->selectRow(0);
|
||||
|
||||
updateWidgets();
|
||||
}
|
||||
|
||||
void ProfileDialog::updateWidgets()
|
||||
{
|
||||
bool enable_del = false;
|
||||
bool enable_del = true;
|
||||
bool enable_ok = true;
|
||||
bool multiple = false;
|
||||
bool contains_user = false;
|
||||
bool enable_import = true;
|
||||
int user_profiles = 0;
|
||||
|
||||
QString msg = "";
|
||||
if ( model_->changesPending() )
|
||||
msg = tr("An import of profiles is not allowed, while changes are pending.");
|
||||
import_button_->setToolTip(msg);
|
||||
import_button_->setEnabled( ! model_->changesPending() );
|
||||
|
||||
QString msg;
|
||||
QModelIndex index = sort_model_->mapToSource(pd_ui_->profileTreeView->currentIndex());
|
||||
QModelIndexList profiles = selectedProfiles();
|
||||
|
||||
/* Ensure that the index is always the name column */
|
||||
if ( index.column() != ProfileModel::COL_NAME )
|
||||
index = index.sibling(index.row(), ProfileModel::COL_NAME);
|
||||
|
||||
if (index.isValid()) {
|
||||
if ( !index.data(ProfileModel::DATA_IS_GLOBAL).toBool() || ! model_->resetDefault())
|
||||
enable_del = true;
|
||||
/* check if more than one viable profile is selected, and inform the sorting model */
|
||||
if ( profiles.count() > 1 )
|
||||
multiple = true;
|
||||
|
||||
/* Check if user profiles have been selected and allow export if it is so */
|
||||
for ( int cnt = 0; cnt < profiles.count(); cnt++ )
|
||||
{
|
||||
if ( ! profiles[cnt].data(ProfileModel::DATA_IS_GLOBAL).toBool() && ! profiles[cnt].data(ProfileModel::DATA_IS_DEFAULT).toBool() )
|
||||
user_profiles++;
|
||||
}
|
||||
if ( user_profiles > 0 )
|
||||
contains_user = true;
|
||||
if ( model_->changesPending() )
|
||||
{
|
||||
enable_import = false;
|
||||
msg = tr("An import of profiles is not allowed, while changes are pending.");
|
||||
}
|
||||
else if ( model_->importPending() )
|
||||
{
|
||||
enable_import = false;
|
||||
msg = tr("An import is pending to be saved. Additional imports are not allowed.");
|
||||
}
|
||||
import_button_->setToolTip( msg );
|
||||
import_button_->setEnabled( enable_import );
|
||||
|
||||
#ifdef HAVE_MINIZIP
|
||||
bool enable_export = false;
|
||||
|
||||
/* enable export if no changes are pending */
|
||||
if ( ! model_->changesPending() )
|
||||
enable_export = true;
|
||||
|
||||
export_button_->setEnabled( enable_export );
|
||||
if ( ! enable_export )
|
||||
{
|
||||
if ( ! contains_user )
|
||||
export_button_->setToolTip(tr("An export of profiles is only allowed for personal profiles."));
|
||||
else
|
||||
export_button_->setToolTip(tr("An export of profiles is not allowed, while changes are pending."));
|
||||
}
|
||||
export_selected_entry_->setVisible(contains_user);
|
||||
#endif
|
||||
|
||||
/* if the current profile is default with reset pending or a global one, deactivate delete */
|
||||
if ( ! multiple )
|
||||
{
|
||||
if ( index.isValid() )
|
||||
{
|
||||
if ( index.data(ProfileModel::DATA_IS_GLOBAL).toBool() )
|
||||
enable_del = false;
|
||||
else if ( index.data(ProfileModel::DATA_IS_DEFAULT).toBool() && model_->resetDefault())
|
||||
enable_del = false;
|
||||
}
|
||||
else if ( ! index.isValid() )
|
||||
enable_del = false;
|
||||
}
|
||||
|
||||
msg.clear();
|
||||
if ( multiple )
|
||||
{
|
||||
/* multiple profiles are being selected, copy is no longer allowed */
|
||||
pd_ui_->copyToolButton->setEnabled(false);
|
||||
|
||||
msg = tr("%Ln selected personal profile(s)", "", user_profiles);
|
||||
pd_ui_->hintLabel->setText(msg);
|
||||
pd_ui_->hintLabel->setUrl(QString());
|
||||
export_selected_entry_->setText(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if only one profile is selected, display it's path in the hint label and activate link (if allowed) */
|
||||
if ( index.isValid() )
|
||||
{
|
||||
QString temp = index.data(ProfileModel::DATA_PATH).toString();
|
||||
if ( index.data(ProfileModel::DATA_PATH_IS_NOT_DESCRIPTION).toBool() )
|
||||
pd_ui_->hintLabel->setUrl(QUrl::fromLocalFile(temp).toString());
|
||||
else
|
||||
pd_ui_->hintLabel->setUrl(QString());
|
||||
pd_ui_->hintLabel->setText(temp);
|
||||
pd_ui_->hintLabel->setToolTip(index.data(Qt::ToolTipRole).toString());
|
||||
|
||||
if ( ! index.data(ProfileModel::DATA_IS_GLOBAL).toBool() && ! index.data(ProfileModel::DATA_IS_DEFAULT).toBool() )
|
||||
msg = tr("%Ln selected personal profile(s)", "", 1);
|
||||
}
|
||||
|
||||
pd_ui_->copyToolButton->setEnabled(true);
|
||||
export_selected_entry_->setText(msg);
|
||||
}
|
||||
|
||||
/* Ensure, that the ok button is disabled, if an invalid name is used or if duplicate global profiles exist */
|
||||
if (model_ && model_->rowCount() > 0)
|
||||
{
|
||||
for ( int row = 0; row < model_->rowCount(); row++ )
|
||||
msg.clear();
|
||||
for ( int row = 0; row < model_->rowCount() && enable_ok; row++ )
|
||||
{
|
||||
QModelIndex idx = model_->index(row, ProfileModel::COL_NAME);
|
||||
QString name = idx.data().toString();
|
||||
|
||||
if ( ! ProfileModel::checkNameValidity(name) )
|
||||
if ( ! ProfileModel::checkNameValidity(name, &msg) )
|
||||
{
|
||||
pd_ui_->hintLabel->setText(msg);
|
||||
pd_ui_->hintLabel->setUrl(QString());
|
||||
|
||||
enable_ok = false;
|
||||
continue;
|
||||
}
|
||||
|
@ -239,25 +347,15 @@ void ProfileDialog::updateWidgets()
|
|||
}
|
||||
}
|
||||
|
||||
pd_ui_->profileTreeView->resizeColumnToContents(0);
|
||||
/* ensure the name column is resized to it's content */
|
||||
pd_ui_->profileTreeView->resizeColumnToContents(ProfileModel::COL_NAME);
|
||||
|
||||
pd_ui_->deleteToolButton->setEnabled(enable_del);
|
||||
ok_button_->setEnabled(enable_ok);
|
||||
}
|
||||
|
||||
void ProfileDialog::currentItemChanged()
|
||||
void ProfileDialog::currentItemChanged(const QModelIndex &, const QModelIndex &)
|
||||
{
|
||||
QModelIndex idx = pd_ui_->profileTreeView->currentIndex();
|
||||
if ( idx.isValid() )
|
||||
{
|
||||
QString temp = idx.data(ProfileModel::DATA_PATH).toString();
|
||||
if ( idx.data(ProfileModel::DATA_PATH_IS_NOT_DESCRIPTION).toBool() )
|
||||
pd_ui_->hintLabel->setUrl(QUrl::fromLocalFile(temp).toString());
|
||||
else
|
||||
pd_ui_->hintLabel->setUrl(QString());
|
||||
pd_ui_->hintLabel->setText(temp);
|
||||
pd_ui_->hintLabel->setToolTip(idx.data(Qt::ToolTipRole).toString());
|
||||
}
|
||||
|
||||
updateWidgets();
|
||||
}
|
||||
|
||||
|
@ -281,11 +379,21 @@ void ProfileDialog::on_newToolButton_clicked()
|
|||
|
||||
void ProfileDialog::on_deleteToolButton_clicked()
|
||||
{
|
||||
QModelIndex index = sort_model_->mapToSource(pd_ui_->profileTreeView->currentIndex());
|
||||
QModelIndexList profiles = selectedProfiles();
|
||||
if ( profiles.count() <= 0 )
|
||||
return;
|
||||
|
||||
model_->deleteEntry(index);
|
||||
model_->deleteEntries(profiles);
|
||||
|
||||
bool isGlobal = model_->activeProfile().data(ProfileModel::DATA_IS_GLOBAL).toBool();
|
||||
int row = model_->findByName(model_->activeProfile().data().toString());
|
||||
/* If the active profile is deleted, the default is selected next */
|
||||
if ( row < 0 )
|
||||
row = 0;
|
||||
QModelIndex newIdx = sort_model_->mapFromSource(model_->index(row, 0));
|
||||
if ( newIdx.data(ProfileModel::DATA_IS_GLOBAL).toBool() != isGlobal )
|
||||
newIdx = sort_model_->mapFromSource(model_->index(0, 0));
|
||||
|
||||
QModelIndex newIdx = sort_model_->mapFromSource(model_->index(0, 0));
|
||||
pd_ui_->profileTreeView->setCurrentIndex(newIdx);
|
||||
|
||||
updateWidgets();
|
||||
|
@ -293,6 +401,10 @@ void ProfileDialog::on_deleteToolButton_clicked()
|
|||
|
||||
void ProfileDialog::on_copyToolButton_clicked()
|
||||
{
|
||||
QModelIndexList profiles = selectedProfiles();
|
||||
if ( profiles.count() > 1 )
|
||||
return;
|
||||
|
||||
pd_ui_->lineProfileFilter->setText("");
|
||||
pd_ui_->cmbProfileTypes->setCurrentIndex(ProfileSortModel::AllProfiles);
|
||||
sort_model_->setFilterString();
|
||||
|
@ -320,8 +432,15 @@ void ProfileDialog::on_buttonBox_accepted()
|
|||
bool item_data_removed = false;
|
||||
|
||||
QModelIndex index = sort_model_->mapToSource(pd_ui_->profileTreeView->currentIndex());
|
||||
|
||||
pd_ui_->buttonBox->setFocus();
|
||||
|
||||
QModelIndexList profiles = selectedProfiles();
|
||||
if ( profiles.count() <= 0 )
|
||||
index = QModelIndex();
|
||||
|
||||
QModelIndex default_item = sort_model_->mapFromSource(model_->index(0, ProfileModel::COL_NAME));
|
||||
if (index.column() != ProfileModel::COL_NAME)
|
||||
if (index.isValid() && index.column() != ProfileModel::COL_NAME)
|
||||
index = index.sibling(index.row(), ProfileModel::COL_NAME);
|
||||
|
||||
if (default_item.data(ProfileModel::DATA_STATUS).toInt() == PROF_STAT_DEFAULT && model_->resetDefault())
|
||||
|
@ -360,7 +479,16 @@ void ProfileDialog::on_buttonBox_accepted()
|
|||
model_->doResetModel();
|
||||
|
||||
QString profileName;
|
||||
if (index.isValid() && !item_data_removed) {
|
||||
|
||||
if ( ! index.isValid() && model_->lastSetRow() >= 0 )
|
||||
{
|
||||
QModelIndex original = model_->index(model_->lastSetRow(), ProfileModel::COL_NAME);
|
||||
index = sort_model_->mapFromSource(original);
|
||||
}
|
||||
|
||||
/* If multiple profiles are selected, do not change the selected profile */
|
||||
if ( index.isValid() && ! item_data_removed && profiles.count() <= 1 )
|
||||
{
|
||||
profileName = model_->data(index).toString();
|
||||
}
|
||||
|
||||
|
@ -374,16 +502,32 @@ void ProfileDialog::on_buttonBox_accepted()
|
|||
}
|
||||
}
|
||||
|
||||
void ProfileDialog::on_buttonBox_rejected()
|
||||
{
|
||||
QString msg;
|
||||
if ( ! model_->clearImported(&msg) )
|
||||
QMessageBox::critical(this, tr("Error"), msg);
|
||||
}
|
||||
|
||||
void ProfileDialog::on_buttonBox_helpRequested()
|
||||
{
|
||||
wsApp->helpTopicAction(HELP_CONFIG_PROFILES_DIALOG);
|
||||
}
|
||||
|
||||
void ProfileDialog::editingFinished()
|
||||
void ProfileDialog::dataChanged(const QModelIndex &idx)
|
||||
{
|
||||
pd_ui_->lineProfileFilter->setText("");
|
||||
pd_ui_->cmbProfileTypes->setCurrentIndex(ProfileSortModel::AllProfiles);
|
||||
currentItemChanged();
|
||||
|
||||
pd_ui_->profileTreeView->setFocus();
|
||||
if ( ! idx.isValid() && model_->lastSetRow() >= 0 )
|
||||
{
|
||||
QModelIndex original = model_->index(model_->lastSetRow(), ProfileModel::COL_NAME);
|
||||
pd_ui_->profileTreeView->setCurrentIndex(sort_model_->mapFromSource(original));
|
||||
pd_ui_->profileTreeView->selectRow(sort_model_->mapFromSource(original).row());
|
||||
}
|
||||
|
||||
updateWidgets();
|
||||
}
|
||||
|
||||
void ProfileDialog::filterChanged(const QString &text)
|
||||
|
@ -404,86 +548,105 @@ void ProfileDialog::filterChanged(const QString &text)
|
|||
}
|
||||
|
||||
#ifdef HAVE_MINIZIP
|
||||
void ProfileDialog::exportProfiles(bool exportAll)
|
||||
void ProfileDialog::exportProfiles(bool exportAllPersonalProfiles)
|
||||
{
|
||||
QAction * action = qobject_cast<QAction *>(sender());
|
||||
if ( action && action->property(PROFILE_EXPORT_PROPERTY).isValid() )
|
||||
exportAll = action->property(PROFILE_EXPORT_PROPERTY).toString().compare(PROFILE_EXPORT_ALL) == 0;
|
||||
exportAllPersonalProfiles = action->property(PROFILE_EXPORT_PROPERTY).toString().compare(PROFILE_EXPORT_ALL) == 0;
|
||||
|
||||
QModelIndexList items;
|
||||
int skipped = 0;
|
||||
|
||||
if ( ! exportAll && pd_ui_->profileTreeView->currentIndex().isValid() )
|
||||
items << sort_model_->mapToSource(pd_ui_->profileTreeView->currentIndex());
|
||||
else if ( exportAll )
|
||||
if ( ! exportAllPersonalProfiles )
|
||||
{
|
||||
foreach ( QModelIndex idx, selectedProfiles() )
|
||||
{
|
||||
if ( ! idx.data(ProfileModel::DATA_IS_GLOBAL).toBool() && ! idx.data(ProfileModel::DATA_IS_DEFAULT).toBool() )
|
||||
items << idx;
|
||||
else
|
||||
skipped++;
|
||||
}
|
||||
}
|
||||
else if ( exportAllPersonalProfiles )
|
||||
{
|
||||
for ( int cnt = 0; cnt < sort_model_->rowCount(); cnt++ )
|
||||
{
|
||||
QModelIndex idx = sort_model_->index(cnt, ProfileModel::COL_NAME);
|
||||
if ( ! idx.data(ProfileModel::DATA_IS_GLOBAL).toBool() && ! idx.data(ProfileModel::DATA_IS_DEFAULT).toBool() )
|
||||
{
|
||||
items << sort_model_->mapToSource(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( items.count() == 0 )
|
||||
{
|
||||
QMessageBox::warning(this, tr("Exporting profiles"), tr("No profiles found for export"));
|
||||
QString msg = tr("No profiles found for export");
|
||||
if ( skipped > 0 )
|
||||
msg.append(tr(", %Ln profile(s) skipped", "", skipped));
|
||||
QMessageBox::critical(this, tr("Exporting profiles"), msg);
|
||||
return;
|
||||
}
|
||||
|
||||
QString zipFile = QFileDialog::getSaveFileName(this, tr("Select zip file for export"), QString(), tr("Zip File (*.zip)"));
|
||||
QString zipFile = QFileDialog::getSaveFileName(this, tr("Select zip file for export"), lastOpenDir(), tr("Zip File (*.zip)"));
|
||||
|
||||
QString err;
|
||||
if ( model_->exportProfiles(zipFile, items, &err) )
|
||||
QMessageBox::information(this, tr("Exporting profiles"), tr("%Ln profile(s) exported", "", items.count()));
|
||||
else
|
||||
QMessageBox::warning(this, tr("Exporting profiles"), QString("%1\n\n%2: %3").arg(tr("An error has occured while exporting profiles")).arg("Error").arg(err));
|
||||
if ( zipFile.length() > 0 )
|
||||
{
|
||||
QFileInfo fi(zipFile);
|
||||
if ( fi.suffix().length() == 0 || fi.suffix().toLower().compare("zip") != 0 )
|
||||
zipFile += ".zip";
|
||||
|
||||
QString err;
|
||||
if ( model_->exportProfiles(zipFile, items, &err) )
|
||||
{
|
||||
QString msg = tr("%Ln profile(s) exported", "", items.count());
|
||||
if ( skipped > 0 )
|
||||
msg.append(tr(", %Ln profile(s) skipped", "", skipped));
|
||||
QMessageBox::information(this, tr("Exporting profiles"), msg);
|
||||
|
||||
QFileInfo zip(zipFile);
|
||||
storeLastDir(zip.absolutePath());
|
||||
}
|
||||
else
|
||||
{
|
||||
QString msg = tr("An error has occurred while exporting profiles");
|
||||
if ( err.length() > 0 )
|
||||
msg.append(QString("\n\n%1: %3").arg(tr("Error")).arg(err));
|
||||
QMessageBox::critical(this, tr("Exporting profiles"), msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileDialog::importFromZip()
|
||||
{
|
||||
QString zipFile = QFileDialog::getOpenFileName(this, tr("Select zip file for import"), QString(), tr("Zip File (*.zip)"));
|
||||
QString zipFile = QFileDialog::getOpenFileName(this, tr("Select zip file for import"), lastOpenDir(), tr("Zip File (*.zip)"));
|
||||
|
||||
QFileInfo fi(zipFile);
|
||||
if ( ! fi.exists() )
|
||||
return;
|
||||
|
||||
int skipped = 0;
|
||||
int count = model_->importProfilesFromZip(zipFile, &skipped);
|
||||
QString msg;
|
||||
QMessageBox::Icon icon;
|
||||
QStringList import;
|
||||
int count = model_->importProfilesFromZip(zipFile, &skipped, &import);
|
||||
|
||||
if ( count == 0 && skipped == 0 )
|
||||
{
|
||||
icon = QMessageBox::Warning;
|
||||
msg = tr("No profiles found for import in %1").arg(fi.fileName());
|
||||
}
|
||||
else
|
||||
{
|
||||
icon = QMessageBox::Information;
|
||||
msg = tr("%Ln profile(s) imported", "", count);
|
||||
if ( skipped > 0 )
|
||||
msg.append(tr(", %Ln profile(s) skipped", "", skipped));
|
||||
}
|
||||
|
||||
QMessageBox msgBox(icon, tr("Importing profiles"), msg, QMessageBox::Ok, this);
|
||||
msgBox.exec();
|
||||
|
||||
if ( count > 0 )
|
||||
resetTreeView();
|
||||
finishImport(fi, count, skipped, import);
|
||||
}
|
||||
#endif
|
||||
|
||||
void ProfileDialog::importFromDirectory()
|
||||
{
|
||||
QString importDir = QFileDialog::getExistingDirectory(this, tr("Select directory for import"), QString());
|
||||
QString importDir = QFileDialog::getExistingDirectory(this, tr("Select directory for import"), lastOpenDir());
|
||||
|
||||
QFileInfo fi(importDir);
|
||||
if ( ! fi.isDir() )
|
||||
return;
|
||||
|
||||
int skipped = 0;
|
||||
int count = model_->importProfilesFromDir(importDir, &skipped);
|
||||
QStringList import;
|
||||
int count = model_->importProfilesFromDir(importDir.append(QDir::separator()), &skipped, false, &import);
|
||||
|
||||
finishImport(fi, count, skipped, import);
|
||||
}
|
||||
|
||||
void ProfileDialog::finishImport(QFileInfo fi, int count, int skipped, QStringList import)
|
||||
{
|
||||
QString msg;
|
||||
QMessageBox::Icon icon;
|
||||
|
||||
|
@ -498,12 +661,62 @@ void ProfileDialog::importFromDirectory()
|
|||
msg = tr("%Ln profile(s) imported", "", count);
|
||||
if ( skipped > 0 )
|
||||
msg.append(tr(", %Ln profile(s) skipped", "", skipped));
|
||||
|
||||
storeLastDir(fi.absolutePath());
|
||||
}
|
||||
|
||||
if ( count > 0 )
|
||||
{
|
||||
import.sort();
|
||||
resetTreeView();
|
||||
model_->markAsImported(import);
|
||||
int rowFirstImported = model_->findByName(import.at(0));
|
||||
QModelIndex idx = sort_model_->mapFromSource(model_->index(rowFirstImported, ProfileModel::COL_NAME));
|
||||
pd_ui_->profileTreeView->selectRow(idx.isValid() ? idx.row() : 0);
|
||||
}
|
||||
|
||||
QMessageBox msgBox(icon, tr("Importing profiles"), msg, QMessageBox::Ok, this);
|
||||
msgBox.exec();
|
||||
if ( count > 0 )
|
||||
resetTreeView();
|
||||
|
||||
updateWidgets();
|
||||
}
|
||||
|
||||
QString ProfileDialog::lastOpenDir()
|
||||
{
|
||||
QString result;
|
||||
|
||||
switch (prefs.gui_fileopen_style) {
|
||||
|
||||
case FO_STYLE_LAST_OPENED:
|
||||
/* The user has specified that we should start out in the last directory
|
||||
we looked in. If we've already opened a file, use its containing
|
||||
directory, if we could determine it, as the directory, otherwise
|
||||
use the "last opened" directory saved in the preferences file if
|
||||
there was one. */
|
||||
/* This is now the default behaviour in file_selection_new() */
|
||||
result = QString(get_last_open_dir());
|
||||
break;
|
||||
|
||||
case FO_STYLE_SPECIFIED:
|
||||
/* The user has specified that we should always start out in a
|
||||
specified directory; if they've specified that directory,
|
||||
start out by showing the files in that dir. */
|
||||
if (prefs.gui_fileopen_dir[0] != '\0')
|
||||
result = QString(prefs.gui_fileopen_dir);
|
||||
break;
|
||||
}
|
||||
|
||||
QDir ld(result);
|
||||
if ( ld.exists() )
|
||||
return result;
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
void ProfileDialog::storeLastDir(QString dir)
|
||||
{
|
||||
if (wsApp && dir.length() > 0)
|
||||
wsApp->setLastOpenDir(dir.toUtf8().constData());
|
||||
}
|
||||
|
||||
void ProfileDialog::resetTreeView()
|
||||
|
@ -512,15 +725,27 @@ void ProfileDialog::resetTreeView()
|
|||
{
|
||||
pd_ui_->profileTreeView->setModel(Q_NULLPTR);
|
||||
sort_model_->setSourceModel(Q_NULLPTR);
|
||||
model_->disconnect();
|
||||
if ( pd_ui_->profileTreeView->selectionModel() )
|
||||
pd_ui_->profileTreeView->selectionModel()->disconnect();
|
||||
delete sort_model_;
|
||||
delete model_;
|
||||
}
|
||||
|
||||
model_ = new ProfileModel(this);
|
||||
sort_model_ = new ProfileSortModel(this);
|
||||
model_ = new ProfileModel(pd_ui_->profileTreeView);
|
||||
sort_model_ = new ProfileSortModel(pd_ui_->profileTreeView);
|
||||
sort_model_->setSourceModel(model_);
|
||||
pd_ui_->profileTreeView->setModel(sort_model_);
|
||||
|
||||
connect(model_, &ProfileModel::itemChanged, this, &ProfileDialog::dataChanged, Qt::QueuedConnection);
|
||||
QItemSelectionModel *selModel = pd_ui_->profileTreeView->selectionModel();
|
||||
connect(selModel, &QItemSelectionModel::currentChanged,
|
||||
this, &ProfileDialog::currentItemChanged, Qt::QueuedConnection);
|
||||
connect(selModel, SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
|
||||
this, SLOT(selectionChanged()));
|
||||
|
||||
selectionChanged();
|
||||
|
||||
if ( sort_model_->columnCount() <= 1 )
|
||||
pd_ui_->profileTreeView->header()->hide();
|
||||
else
|
||||
|
|
|
@ -64,10 +64,14 @@ private:
|
|||
void updateWidgets();
|
||||
void resetTreeView();
|
||||
|
||||
QString lastOpenDir();
|
||||
void storeLastDir(QString dir);
|
||||
void finishImport(QFileInfo fi, int count, int skipped, QStringList import);
|
||||
|
||||
private slots:
|
||||
void currentItemChanged();
|
||||
void currentItemChanged(const QModelIndex & c = QModelIndex(), const QModelIndex & p = QModelIndex());
|
||||
#ifdef HAVE_MINIZIP
|
||||
void exportProfiles(bool exportAll = false);
|
||||
void exportProfiles(bool exportAllPersonalProfiles = false);
|
||||
void importFromZip();
|
||||
#endif
|
||||
void importFromDirectory();
|
||||
|
@ -76,11 +80,15 @@ private slots:
|
|||
void on_deleteToolButton_clicked();
|
||||
void on_copyToolButton_clicked();
|
||||
void on_buttonBox_accepted();
|
||||
void on_buttonBox_rejected();
|
||||
void on_buttonBox_helpRequested();
|
||||
void editingFinished();
|
||||
void dataChanged(const QModelIndex &);
|
||||
|
||||
void filterChanged(const QString &);
|
||||
|
||||
void selectionChanged();
|
||||
QModelIndexList selectedProfiles();
|
||||
|
||||
// QWidget interface
|
||||
|
||||
};
|
||||
|
|
|
@ -27,6 +27,9 @@
|
|||
</item>
|
||||
<item>
|
||||
<widget class="ProfileTreeView" name="profileTreeView">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
|
@ -69,7 +72,7 @@
|
|||
<item>
|
||||
<widget class="StockIconToolButton" name="deleteToolButton">
|
||||
<property name="toolTip">
|
||||
<string>Remove this profile. System provided profiles cannot be removed.</string>
|
||||
<string><html><head/><body><p>Remove this profile. System provided profiles cannot be removed. The default profile will be resetted upon deletion.</p></body></html></string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
|
|
|
@ -29,7 +29,7 @@ void ProfileUrlLinkDelegate::paint(QPainter *painter, const QStyleOptionViewItem
|
|||
|
||||
}
|
||||
|
||||
ProfileTreeEditDelegate::ProfileTreeEditDelegate(QWidget *parent) : QItemDelegate(parent) {}
|
||||
ProfileTreeEditDelegate::ProfileTreeEditDelegate(QWidget *parent) : QItemDelegate(parent), editor_(Q_NULLPTR) {}
|
||||
|
||||
void ProfileTreeEditDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
|
||||
{
|
||||
|
@ -52,38 +52,26 @@ ProfileTreeView::ProfileTreeView(QWidget *parent) :
|
|||
|
||||
void ProfileTreeView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
|
||||
{
|
||||
if ( selected.count() == 0 && deselected.count() > 0 )
|
||||
{
|
||||
QItemSelection newSelection;
|
||||
newSelection << deselected.at(0);
|
||||
selectionModel()->select(newSelection, QItemSelectionModel::ClearAndSelect);
|
||||
if (newSelection.count() > 0)
|
||||
{
|
||||
QModelIndexList selIndex = selectionModel()->selectedIndexes();
|
||||
scrollTo(selIndex.at(0));
|
||||
}
|
||||
}
|
||||
else if ( selected.count() > 1 )
|
||||
{
|
||||
/* If more then one item is selected, only accept the new item, deselect everything else */
|
||||
QSet<QItemSelectionRange> intersection = selected.toSet().intersect(deselected.toSet());
|
||||
QItemSelection newSelection;
|
||||
newSelection << intersection.toList().at(0);
|
||||
selectionModel()->select(newSelection, QItemSelectionModel::ClearAndSelect);
|
||||
if (newSelection.count() > 0)
|
||||
{
|
||||
QModelIndexList selIndex = selectionModel()->selectedIndexes();
|
||||
scrollTo(selIndex.at(0));
|
||||
}
|
||||
}
|
||||
else
|
||||
QTreeView::selectionChanged(selected, deselected);
|
||||
}
|
||||
QTreeView::selectionChanged(selected, deselected);
|
||||
|
||||
void ProfileTreeView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
|
||||
{
|
||||
emit currentItemChanged();
|
||||
QTreeView::currentChanged(current, previous);
|
||||
if ( model() )
|
||||
{
|
||||
int offColumn = model()->columnCount();
|
||||
int idxCount = selectedIndexes().count() / offColumn;
|
||||
int dselCount = deselected.count() > 0 ? deselected.at(0).indexes().count() / offColumn : 0;
|
||||
|
||||
/* Ensure, that the last selected row cannot be deselected */
|
||||
if ( idxCount == 0 && dselCount == 1 )
|
||||
{
|
||||
QModelIndex idx = deselected.at(0).indexes().at(0);
|
||||
/* If the last item is no longer valid or the row is out of bounds, select default */
|
||||
if ( ! idx.isValid() || idx.row() >= model()->rowCount() )
|
||||
idx = model()->index(0, ProfileModel::COL_NAME);
|
||||
selectRow(idx.row());
|
||||
}
|
||||
else if ( selectedIndexes().count() == 0 )
|
||||
selectRow(0);
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileTreeView::clicked(const QModelIndex &index)
|
||||
|
@ -92,7 +80,7 @@ void ProfileTreeView::clicked(const QModelIndex &index)
|
|||
return;
|
||||
|
||||
/* Only paint links for valid paths */
|
||||
if ( index.data(ProfileModel::DATA_PATH_IS_NOT_DESCRIPTION).toBool() )
|
||||
if ( index.data(ProfileModel::DATA_INDEX_VALUE_IS_URL).toBool() )
|
||||
{
|
||||
QString path = QDir::toNativeSeparators(index.data().toString());
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
|
@ -111,3 +99,15 @@ void ProfileTreeView::selectRow(int row)
|
|||
QItemSelectionModel::ClearAndSelect);
|
||||
|
||||
}
|
||||
|
||||
void ProfileTreeView::mouseDoubleClickEvent(QMouseEvent *ev)
|
||||
{
|
||||
/* due to the fact, that we allow only row selection, selected rows are always added with all columns */
|
||||
if ( selectedIndexes().count() <= model()->columnCount() )
|
||||
QTreeView::mouseDoubleClickEvent(ev);
|
||||
}
|
||||
|
||||
bool ProfileTreeView::activeEdit()
|
||||
{
|
||||
return ( state() == QAbstractItemView::EditingState );
|
||||
}
|
||||
|
|
|
@ -31,7 +31,12 @@ class ProfileTreeEditDelegate : public QItemDelegate
|
|||
public:
|
||||
ProfileTreeEditDelegate(QWidget *parent = Q_NULLPTR);
|
||||
|
||||
// QAbstractItemDelegate interface
|
||||
virtual void setEditorData(QWidget *editor, const QModelIndex &index) const;
|
||||
|
||||
private:
|
||||
QWidget * editor_;
|
||||
QModelIndex index_;
|
||||
};
|
||||
|
||||
class ProfileTreeView : public QTreeView
|
||||
|
@ -41,20 +46,23 @@ public:
|
|||
ProfileTreeView(QWidget *parent = nullptr);
|
||||
|
||||
void selectRow(int row);
|
||||
bool activeEdit();
|
||||
|
||||
Q_SIGNALS:
|
||||
void currentItemChanged();
|
||||
void itemUpdated();
|
||||
|
||||
// QWidget interface
|
||||
protected:
|
||||
virtual void mouseDoubleClickEvent(QMouseEvent *event);
|
||||
|
||||
// QAbstractItemView interface
|
||||
protected slots:
|
||||
virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
|
||||
virtual void currentChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
|
||||
virtual void clicked(const QModelIndex &index);
|
||||
|
||||
private:
|
||||
ProfileTreeEditDelegate *delegate_;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -8553,6 +8553,20 @@ For example, use 1 hour to have a new file created every hour on the hour.</sour
|
|||
<source>Select zip file for export</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>… %Ln selected personal profile(s)</source>
|
||||
<translation>
|
||||
<numerusform>… %Ln selected personal profile</numerusform>
|
||||
<numerusform>… %Ln selected personal profiles</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>%Ln selected personal profile(s)</source>
|
||||
<translation>
|
||||
<numerusform>%Ln selected personal profile</numerusform>
|
||||
<numerusform>%Ln selected personal profiles</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>%Ln profile(s) exported</source>
|
||||
<translation>
|
||||
|
@ -8598,6 +8612,13 @@ For example, use 1 hour to have a new file created every hour on the hour.</sour
|
|||
<source>Importing profiles</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>%Ln profile(s) selected</source>
|
||||
<translation type="unfinished">
|
||||
<numerusform>%Ln profile selected</numerusform>
|
||||
<numerusform>%Ln profiles selected</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ProfileModel</name>
|
||||
|
|
Loading…
Reference in New Issue