Commit 9fbdef75 authored by Tomi Leppänen's avatar Tomi Leppänen

Merge branch 'jb49173_user_model' into 'master'

Add model to access user info

See merge request mer-core/nemo-qml-plugin-systemsettings!134
parents 26cc3aef 6eab714d
......@@ -12,6 +12,7 @@ Requires: connman
Requires: mce >= 1.83.0
Requires: libsailfishkeyprovider >= 0.0.14
Requires: connman-qt5 >= 1.2.21
Requires: user-managerd
Requires(post): coreutils
BuildRequires: pkgconfig(Qt5Qml)
BuildRequires: pkgconfig(Qt5SystemInfo)
......@@ -32,6 +33,8 @@ BuildRequires: pkgconfig(ssu-sysinfo) >= 1.1.0
BuildRequires: pkgconfig(packagekitqt5)
BuildRequires: pkgconfig(glib-2.0)
BuildRequires: pkgconfig(sailfishaccesscontrol)
BuildRequires: pkgconfig(libsystemd)
BuildRequires: pkgconfig(sailfishusermanager)
%description
%{summary}.
......
......@@ -34,3 +34,4 @@
Q_LOGGING_CATEGORY(lcVpnLog, "org.sailfishos.settings.vpn", QtWarningMsg)
Q_LOGGING_CATEGORY(lcDeveloperModeLog, "org.sailfishos.settings.developermode", QtWarningMsg)
Q_LOGGING_CATEGORY(lcMemoryCardLog, "org.sailfishos.settings.memorycard", QtWarningMsg)
Q_LOGGING_CATEGORY(lcUsersLog, "org.sailfishos.settings.users", QtWarningMsg)
......@@ -37,5 +37,6 @@
Q_DECLARE_LOGGING_CATEGORY(lcVpnLog)
Q_DECLARE_LOGGING_CATEGORY(lcDeveloperModeLog)
Q_DECLARE_LOGGING_CATEGORY(lcMemoryCardLog)
Q_DECLARE_LOGGING_CATEGORY(lcUsersLog)
#endif
......@@ -51,6 +51,8 @@
#include "locationsettings.h"
#include "deviceinfo.h"
#include "nfcsettings.h"
#include "userinfo.h"
#include "usermodel.h"
template<class T>
static QObject *api_factory(QQmlEngine *, QJSEngine *)
......@@ -91,6 +93,8 @@ public:
qmlRegisterType<LocationSettings>(uri, 1, 0, "LocationSettings");
qmlRegisterType<DeviceInfo>(uri, 1, 0, "DeviceInfo");
qmlRegisterType<NfcSettings>(uri, 1, 0, "NfcSettings");
qmlRegisterType<UserInfo>(uri, 1, 0, "UserInfo");
qmlRegisterType<UserModel>(uri, 1, 0, "UserModel");
}
};
......
......@@ -7,7 +7,7 @@ QT -= gui
CONFIG += c++11 hide_symbols link_pkgconfig
PKGCONFIG += profile mlite5 mce timed-qt5 libshadowutils blkid libcrypto nemomodels-qt5 libsailfishkeyprovider connman-qt5 glib-2.0
PKGCONFIG += ssu-sysinfo nemodbus packagekitqt5
PKGCONFIG += ssu-sysinfo nemodbus packagekitqt5 libsystemd sailfishusermanager
system(qdbusxml2cpp -p mceiface.h:mceiface.cpp mce.xml)
......@@ -37,7 +37,9 @@ SOURCES += \
udisks2block.cpp \
udisks2blockdevices.cpp \
udisks2job.cpp \
udisks2monitor.cpp
udisks2monitor.cpp \
userinfo.cpp \
usermodel.cpp
PUBLIC_HEADERS = \
languagemodel.h \
......@@ -60,7 +62,9 @@ PUBLIC_HEADERS = \
systemsettingsglobal.h \
deviceinfo.h \
locationsettings.h \
timezoneinfo.h
timezoneinfo.h \
userinfo.h \
usermodel.h
HEADERS += \
$$PUBLIC_HEADERS \
......@@ -76,7 +80,8 @@ HEADERS += \
partitionmanager_p.h \
udisks2blockdevices_p.h \
udisks2job_p.h \
udisks2monitor_p.h
udisks2monitor_p.h \
userinfo_p.h
DEFINES += \
SYSTEMSETTINGS_BUILD_LIBRARY
......
/*
* Copyright (C) 2020 Open Mobile Platform LLC.
*
* You may use this file under the terms of the BSD license as follows:
*
* "Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
*/
#include "userinfo.h"
#include "userinfo_p.h"
#include <pwd.h>
#include <sys/types.h>
#include <systemd/sd-login.h>
namespace {
enum SpecialIds : uid_t {
DeviceOwnerId = 100000,
InvalidId = (uid_t)(-1),
};
QString nameFromGecos(const char *gecos)
{
// typically GECOS has (sub)fields separated by ","
// and the first one of them is full name of the user.
// Sometimes it contains just the full name or it might be empty,
// thus do this on best effort basis.
auto name = QString::fromUtf8(gecos);
int i = name.indexOf(QStringLiteral(","));
if (i != -1)
name.truncate(i);
return name;
}
}
UserInfoPrivate::UserInfoPrivate()
: m_uid(InvalidId)
, m_loggedIn(false)
{
}
UserInfoPrivate::UserInfoPrivate(struct passwd *pwd)
: m_uid(pwd->pw_uid)
, m_username(QString::fromUtf8(pwd->pw_name))
, m_name(nameFromGecos(pwd->pw_gecos))
// require_active == false -> both online and active are logged in.
// Specifying seat should make sure that remote users are not
// counted as they don't have seats.
, m_loggedIn(sd_uid_is_on_seat(m_uid, 0, "seat0") > 0)
{
}
UserInfoPrivate::~UserInfoPrivate()
{
}
QWeakPointer<UserInfoPrivate> UserInfoPrivate::s_current;
void UserInfoPrivate::set(struct passwd *pwd)
{
QString username;
QString name;
if (pwd) {
Q_ASSERT(pwd->pw_uid == m_uid);
username = QString::fromUtf8(pwd->pw_name);
name = nameFromGecos(pwd->pw_gecos);
} else if (m_uid != InvalidId) {
m_uid = InvalidId;
emit uidChanged();
}
if (m_username != username) {
m_username = username;
emit usernameChanged();
}
if (m_name != name) {
m_name = name;
emit nameChanged();
}
}
/**
* Construct UserInfo for the current user
*
* If it has been constructed before, this reuses the old data.
*/
UserInfo::UserInfo()
{
if (!UserInfoPrivate::s_current.isNull()) {
d_ptr = QSharedPointer<UserInfoPrivate>(UserInfoPrivate::s_current);
}
if (d_ptr.isNull()) {
uid_t uid = InvalidId;
struct passwd *pwd;
if (sd_seat_get_active("seat0", NULL, &uid) < 0 || uid == InvalidId || !(pwd = getpwuid(uid))) {
d_ptr = QSharedPointer<UserInfoPrivate>(new UserInfoPrivate);
} else {
d_ptr = QSharedPointer<UserInfoPrivate>(new UserInfoPrivate(pwd));
}
// pwd must not be free'd
}
if (current())
UserInfoPrivate::s_current = d_ptr;
connectSignals();
}
UserInfo::UserInfo(const UserInfo &other)
: QObject(other.parent())
, d_ptr(other.d_ptr)
{
connectSignals();
}
/**
* Construct UserInfo by uid
*/
UserInfo::UserInfo(int uid)
{
struct passwd *pwd = (uid_t)uid != InvalidId ? getpwuid((uid_t)uid) : nullptr;
if (pwd) {
d_ptr = QSharedPointer<UserInfoPrivate>(new UserInfoPrivate(pwd));
} else {
d_ptr = QSharedPointer<UserInfoPrivate>(new UserInfoPrivate);
}
// pwd must not be free'd
if (current())
UserInfoPrivate::s_current = d_ptr;
connectSignals();
}
/**
* Construct UserInfo by username
*/
UserInfo::UserInfo(QString username)
{
struct passwd *pwd = getpwnam(username.toUtf8().constData());
if (pwd) {
d_ptr = QSharedPointer<UserInfoPrivate>(new UserInfoPrivate(pwd));
} else {
d_ptr = QSharedPointer<UserInfoPrivate>(new UserInfoPrivate);
}
// pwd must not be free'd
if (current())
UserInfoPrivate::s_current = d_ptr;
connectSignals();
}
/**
* Construct a placeholder user that doesn't exist
*
* Placeholder users are always invalid.
*/
UserInfo UserInfo::placeholder()
{
return UserInfo(InvalidId);
}
UserInfo::~UserInfo()
{
}
/**
* Returns true if user exists
*/
bool UserInfo::isValid() const
{
Q_D(const UserInfo);
return d->m_uid != InvalidId;
}
QString UserInfo::username() const
{
Q_D(const UserInfo);
return d->m_username;
}
void UserInfo::setUsername(QString username)
{
Q_D(UserInfo);
if (d->m_username != username) {
d->m_username = username;
emit d_ptr->usernameChanged();
}
}
QString UserInfo::name() const
{
Q_D(const UserInfo);
return d->m_name;
}
void UserInfo::setName(QString name)
{
Q_D(UserInfo);
if (d->m_name != name) {
d->m_name = name;
emit d_ptr->nameChanged();
}
}
UserInfo::UserType UserInfo::type() const
{
Q_D(const UserInfo);
// Device lock considers user with id 100000 as device owner.
// Some other places consider the user belonging to sailfish-system
// as device owner. We have to pick one here.
return (d->m_uid == DeviceOwnerId) ? DeviceOwner : User;
}
int UserInfo::uid() const
{
Q_D(const UserInfo);
return (int)d->m_uid;
}
/**
* Returs true if user is logged in on seat0 and is the active user, i.e. the current user
*/
bool UserInfo::current() const
{
Q_D(const UserInfo);
// Any logged in user (on seat0) must be the current one
// since we don't have multisession.
return d->m_loggedIn;
}
void UserInfo::reset()
{
Q_D(UserInfo);
d->set((isValid()) ? getpwuid(d->m_uid) : nullptr);
}
UserInfo &UserInfo::operator=(const UserInfo &other)
{
if (this == &other)
return *this;
d_ptr = other.d_ptr;
return *this;
}
bool UserInfo::operator==(const UserInfo &other) const
{
if (!isValid())
return false;
return d_ptr == other.d_ptr;
}
bool UserInfo::operator!=(const UserInfo &other) const
{
if (!isValid())
return true;
return d_ptr != other.d_ptr;
}
void UserInfo::connectSignals()
{
connect(d_ptr.data(), &UserInfoPrivate::usernameChanged, this, &UserInfo::usernameChanged);
connect(d_ptr.data(), &UserInfoPrivate::nameChanged, this, &UserInfo::nameChanged);
connect(d_ptr.data(), &UserInfoPrivate::uidChanged, this, &UserInfo::uidChanged);
}
/*
* Copyright (C) 2020 Open Mobile Platform LLC.
*
* You may use this file under the terms of the BSD license as follows:
*
* "Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
*/
#ifndef USERINFO_H
#define USERINFO_H
#include <QObject>
#include <QList>
#include <QSharedPointer>
#include "systemsettingsglobal.h"
class UserInfoPrivate;
class UserModel;
class SYSTEMSETTINGS_EXPORT UserInfo: public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(UserInfo)
Q_PROPERTY(QString username READ username NOTIFY usernameChanged)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(UserType type READ type CONSTANT)
Q_PROPERTY(int uid READ uid NOTIFY uidChanged)
Q_PROPERTY(bool current READ current CONSTANT)
friend class UserModel;
public:
enum UserType {
User = 0,
DeviceOwner = 1,
};
Q_ENUM(UserType)
UserInfo();
UserInfo(const UserInfo &other);
~UserInfo();
static UserInfo placeholder();
bool isValid() const;
QString username() const;
QString name() const;
UserType type() const;
int uid() const;
bool current() const;
Q_INVOKABLE void reset();
UserInfo &operator=(const UserInfo &other);
bool operator==(const UserInfo &other) const;
bool operator!=(const UserInfo &other) const;
signals:
void usernameChanged();
void nameChanged();
void uidChanged();
private:
explicit UserInfo(int uid);
explicit UserInfo(QString username);
void setUsername(QString username);
void setName(QString name);
void connectSignals();
QSharedPointer<UserInfoPrivate> d_ptr;
};
#endif /* USERINFO_H */
/*
* Copyright (C) 2020 Open Mobile Platform LLC.
*
* You may use this file under the terms of the BSD license as follows:
*
* "Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
*/
#ifndef USERINFOPRIVATE_H
#define USERINFOPRIVATE_H
#include <QObject>
#include <QString>
#include <QWeakPointer>
class UserInfoPrivate : public QObject
{
Q_OBJECT
public:
UserInfoPrivate();
UserInfoPrivate(struct passwd *pwd);
~UserInfoPrivate();
uid_t m_uid;
QString m_username;
QString m_name;
bool m_loggedIn;
static QWeakPointer<UserInfoPrivate> s_current;
void set(struct passwd *pwd);
signals:
void usernameChanged();
void nameChanged();
void uidChanged();
};
#endif /* USERINFOPRIVATE_H */
/*
* Copyright (C) 2020 Open Mobile Platform LLC.
*
* You may use this file under the terms of the BSD license as follows:
*
* "Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
*/
#include "usermodel.h"
#include "logging_p.h"
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDBusInterface>
#include <QDBusMetaType>
#include <QDBusPendingCall>
#include <QDBusPendingReply>
#include <QDBusServiceWatcher>
#include <QString>
#include <functional>
#include <sailfishusermanagerinterface.h>
#include <grp.h>
#include <sys/types.h>
namespace {
const auto UserManagerService = QStringLiteral(SAILFISH_USERMANAGER_DBUS_INTERFACE);
const auto UserManagerPath = QStringLiteral(SAILFISH_USERMANAGER_DBUS_OBJECT_PATH);
const auto UserManagerInterface = QStringLiteral(SAILFISH_USERMANAGER_DBUS_INTERFACE);
}
UserModel::UserModel(QObject *parent)
: QAbstractListModel(parent)
, m_dBusInterface(nullptr)
, m_dBusWatcher(new QDBusServiceWatcher(UserManagerService, QDBusConnection::systemBus(),
QDBusServiceWatcher::WatchForRegistration | QDBusServiceWatcher::WatchForUnregistration, this))
{
qDBusRegisterMetaType<SailfishUserManagerEntry>();
connect(m_dBusWatcher, &QDBusServiceWatcher::serviceRegistered,
this, &UserModel::createInterface);
connect(m_dBusWatcher, &QDBusServiceWatcher::serviceUnregistered,
this, &UserModel::destroyInterface);
if (QDBusConnection::systemBus().interface()->isServiceRegistered(UserManagerService))
createInterface();
struct group *grp = getgrnam("users");
for (int i = 0; grp->gr_mem[i] != nullptr; ++i) {
UserInfo user(QString(grp->gr_mem[i]));
if (user.isValid()) { // Skip invalid users here
m_users.append(user);
m_uidsToRows.insert(user.uid(), m_users.count()-1);
}
}
// grp must not be free'd
}
UserModel::~UserModel()
{
}
bool UserModel::placeholder()
{
// Placeholder is always last and the only item that can be invalid
if (m_users.count() == 0)
return false;
return !m_users.last().isValid();
}
void UserModel::setPlaceholder(bool value)
{
if (placeholder() == value)
return;
if (value) {
auto row = m_users.count();
beginInsertRows(QModelIndex(), row, row);
m_users.append(UserInfo::placeholder());
endInsertRows();
} else {
auto row = m_users.count()-1;
beginRemoveRows(QModelIndex(), row, row);
m_users.remove(row);
endRemoveRows();
}
emit placeholderChanged();
}
QHash<int, QByteArray> UserModel::roleNames() const
{
static const QHash<int, QByteArray> roles = {
{ Qt::DisplayRole, "displayName" },
{ UsernameRole, "username" },
{ NameRole, "name" },
{ TypeRole, "type" },
{ UidRole, "uid" },
{ CurrentRole, "current" },
{ PlaceholderRole, "placeholder" },
};
return roles;
}
int UserModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_users.count();
}
QVariant UserModel::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= m_users.count() || index.column() != 0)
return QVariant();
const UserInfo &user = m_users.at(index.row());
switch (role) {
case Qt::DisplayRole:
return (user.name().isEmpty()) ? user.username() : user.name();
case UsernameRole:
return user.username();
case NameRole:
return user.name();
case TypeRole:
return user.type();
case UidRole:
return user.uid();
case CurrentRole:
return user.current();
case PlaceholderRole:
return !user.isValid();
default:
return QVariant();
}
}
bool UserModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.row() < 0 || index.row() >= m_users.count() || index.column() != 0)
return false;
UserInfo &user = m_users[index.row()];
switch (role) {
case NameRole: {
QString name = value.toString();
if (name.isEmpty() || name == user.name())
return false;
user.setName(name);
if (user.isValid()) {
createInterface();
auto call = m_dBusInterface->asyncCall(QStringLiteral("modifyUser"), (uint)user.uid(), name);
auto *watcher = new QDBusPendingCallWatcher(call, this);
connect(watcher, &QDBusPendingCallWatcher::finished,
this, std::bind(&UserModel::userModifyFinished, this, std::placeholders::_1, index.row()));
}
emit dataChanged(index, index, QVector<int>() << role);
return true;
}
case Qt::DisplayRole:
case UsernameRole:
case TypeRole:
case UidRole:
case CurrentRole:
case PlaceholderRole:
default:
return false;
}
}
QModelIndex UserModel::index(int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(parent);
if (row < 0 || row >= m_users.count() || column != 0)
return QModelIndex();
// create index
return createIndex(row, 0, row);
}
/*
* Creates new user from a placeholder user.
*
* Does nothing if there is no placeholder or user's name is not set.
*/
void UserModel::createUser()
{
if (!placeholder())
return;
auto user = m_users.last();
if (user.name().isEmpty())
return;