qTox  Version: nightly | Commit: bc751c8e1cac455f9690654fcfe0f560d2d7dfdd
profileform.cpp
Go to the documentation of this file.
1 /*
2  Copyright © 2014-2019 by The qTox Project Contributors
3 
4  This file is part of qTox, a Qt-based graphical interface for Tox.
5 
6  qTox is libre software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  qTox is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with qTox. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "profileform.h"
21 #include "ui_profileform.h"
22 #include "src/core/core.h"
30 #include "src/widget/gui.h"
32 #include "src/widget/style.h"
34 #include "src/widget/translator.h"
35 #include "src/widget/widget.h"
36 #include <QApplication>
37 #include <QBuffer>
38 #include <QClipboard>
39 #include <QComboBox>
40 #include <QFileDialog>
41 #include <QGroupBox>
42 #include <QImageReader>
43 #include <QInputDialog>
44 #include <QLabel>
45 #include <QLineEdit>
46 #include <QMap>
47 #include <QMenu>
48 #include <QMessageBox>
49 #include <QMouseEvent>
50 #include <QWindow>
51 
52 static const QMap<IProfileInfo::SetAvatarResult, QString> SET_AVATAR_ERROR = {
54  ProfileForm::tr("Unable to open this file.") },
56  ProfileForm::tr("Unable to read this image.") },
58  ProfileForm::tr("The supplied image is too large.\nPlease use another image.") },
60  ProfileForm::tr("Empty path is unavaliable") },
61 };
62 
63 static const QMap<IProfileInfo::RenameResult, QPair<QString, QString>> RENAME_ERROR = {
65  { ProfileForm::tr("Failed to rename"),
66  ProfileForm::tr("Couldn't rename the profile to \"%1\"") }
67  } ,
69  { ProfileForm::tr("Profile already exists"),
70  ProfileForm::tr("A profile named \"%1\" already exists.") }
71  },
73  { ProfileForm::tr("Empty name"),
74  ProfileForm::tr("Empty name is unavaliable") }
75  },
76 };
77 
78 static const QMap<IProfileInfo::SaveResult, QPair<QString, QString>> SAVE_ERROR = {
80  { ProfileForm::tr("Location not writable", "Title of permissions popup"),
81  ProfileForm::tr("You do not have permission to write to that location. Choose "
82  "another, or cancel the save dialog.", "text of permissions popup") },
83  },
85  { ProfileForm::tr("Failed to save file"),
86  ProfileForm::tr("The file you chose could not be saved.") }
87  },
89  { ProfileForm::tr("Empty path"),
90  ProfileForm::tr("Empty path is unavaliable.") }
91  },
92 };
93 
94 static const QPair<QString, QString> CAN_NOT_CHANGE_PASSWORD = {
95  ProfileForm::tr("Couldn't change password"),
96  ProfileForm::tr("Couldn't change database password, "
97  "it may be corrupted or use the old password.")
98 };
99 
100 ProfileForm::ProfileForm(IProfileInfo* profileInfo, QWidget* parent)
101  : QWidget{parent}
102  , qr{nullptr}
103  , profileInfo{profileInfo}
104 {
105  bodyUI = new Ui::IdentitySettings;
106  bodyUI->setupUi(this);
107 
108  const uint32_t maxNameLength = tox_max_name_length();
109  const QString toolTip = tr("Tox user names cannot exceed %1 characters.").arg(maxNameLength);
110  bodyUI->userNameLabel->setToolTip(toolTip);
111  bodyUI->userName->setMaxLength(static_cast<int>(maxNameLength));
112 
113  // tox
114  toxId = new ClickableTE();
115  toxId->setFont(Style::getFont(Style::Small));
116  toxId->setToolTip(bodyUI->toxId->toolTip());
117 
118  QVBoxLayout* toxIdGroup = qobject_cast<QVBoxLayout*>(bodyUI->toxGroup->layout());
119  delete toxIdGroup->replaceWidget(bodyUI->toxId, toxId); // Original toxId is in heap, delete it
120  bodyUI->toxId->hide();
121 
122  bodyUI->qrLabel->setWordWrap(true);
123 
124  profilePicture = new MaskablePixmapWidget(this, QSize(64, 64), ":/img/avatar_mask.svg");
125  profilePicture->setPixmap(QPixmap(":/img/contact_dark.svg"));
126  profilePicture->setContextMenuPolicy(Qt::CustomContextMenu);
127  profilePicture->setClickable(true);
128  profilePicture->setObjectName("selfAvatar");
129  profilePicture->installEventFilter(this);
130  profilePicture->setAccessibleName("Profile avatar");
131  profilePicture->setAccessibleDescription("Set a profile avatar shown to all contacts");
132  profilePicture->setStyleSheet(Style::getStylesheet("window/profile.css"));
133  connect(profilePicture, &MaskablePixmapWidget::clicked, this, &ProfileForm::onAvatarClicked);
134  connect(profilePicture, &MaskablePixmapWidget::customContextMenuRequested,
136 
137  QHBoxLayout* publicGrouplayout = qobject_cast<QHBoxLayout*>(bodyUI->publicGroup->layout());
138  publicGrouplayout->insertWidget(0, profilePicture);
139  publicGrouplayout->insertSpacing(1, 7);
140 
141  timer.setInterval(750);
142  timer.setSingleShot(true);
143  connect(&timer, &QTimer::timeout, this, [=]() {
144  bodyUI->toxIdLabel->setText(bodyUI->toxIdLabel->text().replace(" ✔", ""));
145  hasCheck = false;
146  });
147 
148  connect(bodyUI->toxIdLabel, &CroppingLabel::clicked, this, &ProfileForm::copyIdClicked);
149  connect(toxId, &ClickableTE::clicked, this, &ProfileForm::copyIdClicked);
150  profileInfo->connectTo_idChanged(this, [=](const ToxId& id) { setToxId(id); });
151  connect(bodyUI->userName, &QLineEdit::editingFinished, this, &ProfileForm::onUserNameEdited);
152  connect(bodyUI->statusMessage, &QLineEdit::editingFinished,
154  connect(bodyUI->renameButton, &QPushButton::clicked, this, &ProfileForm::onRenameClicked);
155  connect(bodyUI->exportButton, &QPushButton::clicked, this, &ProfileForm::onExportClicked);
156  connect(bodyUI->deleteButton, &QPushButton::clicked, this, &ProfileForm::onDeleteClicked);
157  connect(bodyUI->logoutButton, &QPushButton::clicked, this, &ProfileForm::onLogoutClicked);
158  connect(bodyUI->deletePassButton, &QPushButton::clicked,
160  connect(bodyUI->changePassButton, &QPushButton::clicked,
162  connect(bodyUI->deletePassButton, &QPushButton::clicked,
164  connect(bodyUI->changePassButton, &QPushButton::clicked,
166  connect(bodyUI->saveQr, &QPushButton::clicked, this, &ProfileForm::onSaveQrClicked);
167  connect(bodyUI->copyQr, &QPushButton::clicked, this, &ProfileForm::onCopyQrClicked);
168 
169  profileInfo->connectTo_usernameChanged(
170  this,
171  [=](const QString& val) { bodyUI->userName->setText(val); });
172  profileInfo->connectTo_statusMessageChanged(
173  this,
174  [=](const QString& val) { bodyUI->statusMessage->setText(val); });
175 
176  for (QComboBox* cb : findChildren<QComboBox*>()) {
177  cb->installEventFilter(this);
178  cb->setFocusPolicy(Qt::StrongFocus);
179  }
180 
181  retranslateUi();
183 }
184 
186 {
187  const QString name = profileInfo->getProfileName();
188  bodyUI->prFileLabel->setText(tr("Current profile: ") + name + ".tox");
189 }
190 
192 {
194  delete qr;
195  delete bodyUI;
196 }
197 
199 {
200  if (profilePicture->isVisible()) {
201  window()->windowHandle()->alert(0);
202  return true;
203  }
204 
205  return false;
206 }
207 
208 void ProfileForm::show(ContentLayout* contentLayout)
209 {
210  contentLayout->mainContent->layout()->addWidget(this);
211  QWidget::show();
213  bool portable = Settings::getInstance().getMakeToxPortable();
214  QString defaultPath = QDir(Settings::getInstance().getPaths().getSettingsDirPath()).path().trimmed();
215  QString appPath = QApplication::applicationDirPath();
216  QString dirPath = portable ? appPath : defaultPath;
217 
218  QString dirPrLink =
219  tr("Current profile location: %1").arg(QString("<a href=\"file://%1\">%1</a>").arg(dirPath));
220 
221  bodyUI->dirPrLink->setText(dirPrLink);
222  bodyUI->dirPrLink->setOpenExternalLinks(true);
223  bodyUI->dirPrLink->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByMouse);
224  bodyUI->dirPrLink->setMaximumSize(bodyUI->dirPrLink->sizeHint());
225  bodyUI->userName->setFocus();
226  bodyUI->userName->selectAll();
227 }
228 
229 bool ProfileForm::eventFilter(QObject* object, QEvent* event)
230 {
231  if (object == static_cast<QObject*>(profilePicture) && event->type() == QEvent::MouseButtonPress) {
232  QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
233  if (mouseEvent->button() == Qt::RightButton)
234  return true;
235  }
236  return false;
237 }
238 
240 {
241  const QPoint pos = profilePicture->mapToGlobal(point);
242 
243  QMenu contextMenu;
244  const QIcon icon = style()->standardIcon(QStyle::SP_DialogCancelButton);
245  const QAction* removeAction = contextMenu.addAction(icon, tr("Remove"));
246  const QAction* selectedItem = contextMenu.exec(pos);
247 
248  if (selectedItem == removeAction) {
250  }
251 }
252 
254 {
255  profileInfo->copyId();
256  if (!hasCheck) {
257  bodyUI->toxIdLabel->setText(bodyUI->toxIdLabel->text() + " ✔");
258  hasCheck = true;
259  }
260 
261  timer.start();
262 }
263 
265 {
266  profileInfo->setUsername(bodyUI->userName->text());
267 }
268 
270 {
271  profileInfo->setStatusMessage(bodyUI->statusMessage->text());
272 }
273 
274 void ProfileForm::onSelfAvatarLoaded(const QPixmap& pic)
275 {
277 }
278 
280 {
281  QString idString = id.toString();
282  static const QString ToxIdColor = QStringLiteral("%1"
283  "<span style='color:blue'>%2</span>"
284  "<span style='color:gray'>%3</span>");
285  toxId->setText(ToxIdColor
286  .arg(idString.mid(0, 64))
287  .arg(idString.mid(64, 8))
288  .arg(idString.mid(72, 4)));
289 
290  delete qr;
291  qr = new QRWidget();
292  qr->setQRData("tox:" + idString);
293  bodyUI->qrCode->setPixmap(QPixmap::fromImage(qr->getImage()->scaledToWidth(150)));
294 }
295 
297 {
298  QString res;
299  for (auto type : QImageReader::supportedImageFormats()) {
300  res += QString("*.%1 ").arg(QString(type));
301  }
302 
303  return tr("Images (%1)", "filetype filter").arg(res.left(res.size() - 1));
304 }
305 
307 {
308  const QString filter = getSupportedImageFilter();
309  const QString path = QFileDialog::getOpenFileName(Q_NULLPTR, tr("Choose a profile picture"),
310  QDir::homePath(), filter, nullptr);
311 
312  if (path.isEmpty()) {
313  return;
314  }
316  if (result == IProfileInfo::SetAvatarResult::OK) {
317  return;
318  }
319 
320  GUI::showError(tr("Error"), SET_AVATAR_ERROR[result]);
321 }
322 
324 {
325  const QString cur = profileInfo->getProfileName();
326  const QString title = tr("Rename \"%1\"", "renaming a profile").arg(cur);
327  const QString name = QInputDialog::getText(this, title, title + ":");
328  if (name.isEmpty()) {
329  return;
330  }
331 
333  if (result == IProfileInfo::RenameResult::OK) {
334  return;
335  }
336 
337  const QPair<QString, QString> error = RENAME_ERROR[result];
338  GUI::showError(error.first, error.second.arg(name));
340 }
341 
343 {
344  const QString current = profileInfo->getProfileName() + Core::TOX_EXT;
345  //:save dialog title
346  const QString path = QFileDialog::getSaveFileName(Q_NULLPTR, tr("Export profile"), current,
347  //: save dialog filter
348  tr("Tox save file (*.tox)"));
349  if (path.isEmpty()) {
350  return;
351  }
352 
353  const IProfileInfo::SaveResult result = profileInfo->exportProfile(path);
354  if (result == IProfileInfo::SaveResult::OK) {
355  return;
356  }
357 
358  const QPair<QString, QString> error = SAVE_ERROR[result];
359  GUI::showWarning(error.first, error.second);
360 }
361 
363 {
364  const QString title = tr("Delete profile", "deletion confirmation title");
365  const QString question = tr("Are you sure you want to delete this profile?",
366  "deletion confirmation text");
367  if (!GUI::askQuestion(title, question)) {
368  return;
369  }
370 
371  const QStringList manualDeleteFiles = profileInfo->removeProfile();
372  if (manualDeleteFiles.empty()) {
373  return;
374  }
375 
376  //: deletion failed text part 1
377  QString message = tr("The following files could not be deleted:") + "\n\n";
378  for (const QString& file : manualDeleteFiles) {
379  message += file + "\n";
380  }
381 
382  //: deletion failed text part 2
383  message += "\n" + tr("Please manually remove them.");
384 
385  GUI::showError(tr("Files could not be deleted!", "deletion failed title"), message);
386 }
387 
389 {
390  profileInfo->logout();
391 }
392 
394 {
395  if (profileInfo->isEncrypted()) {
396  bodyUI->changePassButton->setText(tr("Change password", "button text"));
397  bodyUI->deletePassButton->setVisible(true);
398  } else {
399  bodyUI->changePassButton->setText(tr("Set profile password", "button text"));
400  bodyUI->deletePassButton->setVisible(false);
401  }
402 }
403 
405 {
407 }
408 
410 {
411  const QString current = profileInfo->getProfileName() + ".png";
412  const QString path = QFileDialog::getSaveFileName(
413  Q_NULLPTR, tr("Save", "save qr image"), current,
414  tr("Save QrCode (*.png)", "save dialog filter"));
415  if (path.isEmpty()) {
416  return;
417  }
418 
419  const IProfileInfo::SaveResult result = profileInfo->saveQr(*qr->getImage(), path);
420  if (result == IProfileInfo::SaveResult::OK) {
421  return;
422  }
423 
424  const QPair<QString, QString> error = SAVE_ERROR[result];
425  GUI::showWarning(error.first, error.second);
426 }
427 
429 {
430  if (!profileInfo->isEncrypted()) {
431  GUI::showInfo(tr("Nothing to remove"), tr("Your profile does not have a password!"));
432  return;
433  }
434 
435  const QString title = tr("Remove password", "deletion confirmation title");
436  //: deletion confirmation text
437  const QString body = tr("Are you sure you want to remove your password?");
438  if (!GUI::askQuestion(title, body)) {
439  return;
440  }
441 
442  if (!profileInfo->deletePassword()) {
443  GUI::showInfo(CAN_NOT_CHANGE_PASSWORD.first, CAN_NOT_CHANGE_PASSWORD.second);
444  }
445 }
446 
448 {
449  const QString title = tr("Please enter a new password.");
450  SetPasswordDialog* dialog = new SetPasswordDialog(title, QString{}, nullptr);
451  if (dialog->exec() == QDialog::Rejected) {
452  return;
453  }
454 
455  QString newPass = dialog->getPassword();
456  if (!profileInfo->setPassword(newPass)) {
457  GUI::showInfo(CAN_NOT_CHANGE_PASSWORD.first, CAN_NOT_CHANGE_PASSWORD.second);
458  }
459 }
460 
462 {
463  bodyUI->retranslateUi(this);
465  // We have to add the toxId tooltip here and not in the .ui or Qt won't know how to translate it
466  // dynamically
467  toxId->setToolTip(tr("This ID allows other Tox users to add and contact you.\n"
468  "Share it with your friends to begin chatting.\n\n"
469  "This ID includes the NoSpam code (in blue), and the checksum (in gray)."));
470 }
Style::getFont
static QFont getFont(Font font)
Definition: style.cpp:214
profileform.h
profile.h
SetPasswordDialog
Definition: setpassworddialog.h:28
ProfileForm::onExportClicked
void onExportClicked()
Definition: profileform.cpp:342
style.h
IProfileInfo
Definition: iprofileinfo.h:28
ProfileForm::show
void show(ContentLayout *contentLayout)
Definition: profileform.cpp:208
IProfileInfo::SaveResult::EmptyPath
@ EmptyPath
QRWidget
Definition: qrwidget.h:25
ProfileForm::ProfileForm
ProfileForm(IProfileInfo *profileInfo, QWidget *parent=nullptr)
Definition: profileform.cpp:100
ProfileForm::setPasswordButtonsText
void setPasswordButtonsText()
Definition: profileform.cpp:393
settings.h
IProfileInfo::setPassword
virtual bool setPassword(const QString &password)=0
IProfileInfo::RenameResult
RenameResult
Definition: iprofileinfo.h:31
QRWidget::setQRData
void setQRData(const QString &data)
Definition: qrwidget.cpp:56
ProfileForm::onStatusMessageEdited
void onStatusMessageEdited()
Definition: profileform.cpp:269
SetPasswordDialog::getPassword
QString getPassword()
Definition: setpassworddialog.cpp:105
IProfileInfo::SaveResult::NoWritePermission
@ NoWritePermission
ProfileForm::~ProfileForm
~ProfileForm()
Definition: profileform.cpp:191
HistMessageContentType::file
@ file
setpassworddialog.h
IProfileInfo::renameProfile
virtual RenameResult renameProfile(const QString &name)=0
IProfileInfo::isEncrypted
virtual bool isEncrypted() const =0
Translator::unregister
static void unregister(void *owner)
Unregisters all handlers of an owner.
Definition: translator.cpp:103
Core::TOX_EXT
static const QString TOX_EXT
Definition: core.h:89
MaskablePixmapWidget::setPixmap
void setPixmap(const QPixmap &pmap)
Definition: maskablepixmapwidget.cpp:54
IProfileInfo::getProfileName
virtual QString getProfileName() const =0
ProfileForm::onSaveQrClicked
void onSaveQrClicked()
Definition: profileform.cpp:409
ProfileForm::onRenameClicked
void onRenameClicked()
Definition: profileform.cpp:323
GUI::askQuestion
static bool askQuestion(const QString &title, const QString &msg, bool defaultAns=false, bool warning=true, bool yesno=true)
Asks the user a question with Ok/Cancel or Yes/No buttons.
Definition: gui.cpp:169
ProfileForm::hasCheck
bool hasCheck
Definition: profileform.h:91
IProfileInfo::SetAvatarResult::TooLarge
@ TooLarge
ProfileForm::prFileLabelUpdate
void prFileLabelUpdate()
Definition: profileform.cpp:185
IProfileInfo::deletePassword
virtual bool deletePassword()=0
croppinglabel.h
ProfileForm::setToxId
void setToxId(const ToxId &id)
Definition: profileform.cpp:279
iprofileinfo.h
IProfileInfo::copyQr
virtual void copyQr(const QImage &image) const =0
IProfileInfo::exportProfile
virtual SaveResult exportProfile(const QString &path) const =0
IProfileInfo::SetAvatarResult::CanNotRead
@ CanNotRead
IProfileInfo::logout
virtual void logout()=0
IProfileInfo::setAvatar
virtual SetAvatarResult setAvatar(const QString &path)=0
ClickableTE::clicked
void clicked()
IProfileInfo::setUsername
virtual void setUsername(const QString &name)=0
ProfileForm::onDeletePassClicked
void onDeletePassClicked()
Definition: profileform.cpp:428
HistMessageContentType::message
@ message
ProfileForm::profileInfo
IProfileInfo * profileInfo
Definition: profileform.h:94
ProfileForm::copyIdClicked
void copyIdClicked()
Definition: profileform.cpp:253
MaskablePixmapWidget
Definition: maskablepixmapwidget.h:24
IProfileInfo::setStatusMessage
virtual void setStatusMessage(const QString &status)=0
IProfileInfo::SaveResult
SaveResult
Definition: iprofileinfo.h:35
GUI::showInfo
static void showInfo(const QString &title, const QString &msg)
Show some text to the user.
Definition: gui.cpp:115
ProfileForm::retranslateUi
void retranslateUi()
Definition: profileform.cpp:461
ProfileForm::toxId
ClickableTE * toxId
Definition: profileform.h:93
IProfileInfo::RenameResult::OK
@ OK
widget.h
ProfileForm::profilePicture
MaskablePixmapWidget * profilePicture
Definition: profileform.h:89
profilelocker.h
maskablepixmapwidget.h
ProfileForm::onDeleteClicked
void onDeleteClicked()
Definition: profileform.cpp:362
Settings::getMakeToxPortable
bool getMakeToxPortable() const
Definition: settings.cpp:865
IProfileInfo::saveQr
virtual SaveResult saveQr(const QImage &image, const QString &path) const =0
ClickableTE
Definition: profileform.h:37
IProfileInfo::RenameResult::EmptyName
@ EmptyName
Translator::registerHandler
static void registerHandler(const std::function< void()> &, void *owner)
Register a function to be called when the UI needs to be retranslated.
Definition: translator.cpp:93
ToxId
This class represents a Tox ID.
Definition: toxid.h:29
Settings::getInstance
static Settings & getInstance()
Returns the singleton instance.
Definition: settings.cpp:88
ProfileForm::onSelfAvatarLoaded
void onSelfAvatarLoaded(const QPixmap &pic)
Definition: profileform.cpp:274
ProfileForm::showProfilePictureContextMenu
void showProfilePictureContextMenu(const QPoint &point)
Definition: profileform.cpp:239
ProfileForm::onCopyQrClicked
void onCopyQrClicked()
Definition: profileform.cpp:404
GUI::showError
static void showError(const QString &title, const QString &msg)
Show an error to the user.
Definition: gui.cpp:145
contentlayout.h
GUI::showWarning
static void showWarning(const QString &title, const QString &msg)
Show a warning to the user.
Definition: gui.cpp:130
settingswidget.h
Style::getStylesheet
static const QString getStylesheet(const QString &filename, const QFont &baseFont=QFont())
Definition: style.cpp:165
IProfileInfo::SetAvatarResult
SetAvatarResult
Definition: iprofileinfo.h:39
IProfileInfo::SetAvatarResult::CanNotOpen
@ CanNotOpen
IProfileInfo::SetAvatarResult::OK
@ OK
ProfileForm::onUserNameEdited
void onUserNameEdited()
Definition: profileform.cpp:264
core.h
ProfileForm::onChangePassClicked
void onChangePassClicked()
Definition: profileform.cpp:447
IProfileInfo::RenameResult::Error
@ Error
ContentLayout::mainContent
QWidget * mainContent
Definition: contentlayout.h:36
ContentLayout
Definition: contentlayout.h:25
ProfileForm::onLogoutClicked
void onLogoutClicked()
Definition: profileform.cpp:388
ProfileForm::onAvatarClicked
void onAvatarClicked()
Definition: profileform.cpp:306
IProfileInfo::copyId
virtual void copyId() const =0
gui.h
ProfileForm::isShown
bool isShown() const
Definition: profileform.cpp:198
Style::Small
@ Small
Definition: style.h:61
translator.h
CroppingLabel::clicked
void clicked()
ProfileForm::eventFilter
bool eventFilter(QObject *object, QEvent *event)
Definition: profileform.cpp:229
ProfileForm::timer
QTimer timer
Definition: profileform.h:90
ProfileForm::getSupportedImageFilter
static QString getSupportedImageFilter()
Definition: profileform.cpp:296
ProfileForm::bodyUI
Ui::IdentitySettings * bodyUI
Definition: profileform.h:88
IProfileInfo::removeProfile
virtual QStringList removeProfile()=0
IProfileInfo::SaveResult::Error
@ Error
MaskablePixmapWidget::clicked
void clicked()
QRWidget::getImage
QImage * getImage()
Definition: qrwidget.cpp:62
ProfileForm::qr
QRWidget * qr
Definition: profileform.h:92
IProfileInfo::removeAvatar
virtual void removeAvatar()=0
IProfileInfo::RenameResult::ProfileAlreadyExists
@ ProfileAlreadyExists
IProfileInfo::SetAvatarResult::EmptyPath
@ EmptyPath
IProfileInfo::SaveResult::OK
@ OK