SelectDestinationDialog.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "EditorDefs.h"
  9. #include "SelectDestinationDialog.h"
  10. // Qt
  11. #include <QValidator>
  12. #include <QSettings>
  13. #include <QStyle>
  14. #include <QPushButton>
  15. AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
  16. #include <AssetImporter/UI/ui_SelectDestinationDialog.h>
  17. AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
  18. static const char* g_assetProcessorLink = "<a href=\"https://www.o3de.org/docs/user-guide/assets/asset-processor/\">Asset Processor</a>";
  19. static const char* g_copyFilesMessage = "The original file will remain outside of the project and the %1 will not monitor the file.";
  20. static const char* g_moveFilesMessage = "The original file will be moved inside of the project and the %1 will monitor the file for changes.";
  21. static const char* g_selectDestinationFilesPath = "AssetImporter/SelectDestinationFilesPath";
  22. static const char* g_toolTipInvalidRoot = "<p style='white-space:pre'> <span style=\"color:#cccccc;\">Invalid directory. Please choose a destination directory within your game project: %1.</span> </p>";
  23. static const char* g_toolTipPathMustExist = "<p style='white-space:pre'> <span style=\"color:#cccccc;\">Invalid directory. Please choose a destination directory that exists.</span> </p>";
  24. static const char* g_toolTipPathMustBeDirectory = "<p style='white-space:pre'> <span style=\"color:#cccccc;\">Invalid directory. Please choose a valid destination directory.</span> </p>";
  25. static const char* g_toolTipInvalidLength = "<p style='white-space:pre'> <span style=\"color:#cccccc;\">Invalid directory name length. Please choose a destination path that has fewer than %1 characters.</span> </p>";
  26. namespace
  27. {
  28. static QString GetAbsoluteRootDirectoryPath()
  29. {
  30. QDir gameRoot(Path::GetEditingGameDataFolder().c_str());
  31. return gameRoot.absolutePath();
  32. }
  33. }
  34. class DestinationDialogValidator
  35. : public QValidator
  36. {
  37. public:
  38. DestinationDialogValidator(QObject* parent)
  39. : QValidator(parent)
  40. , m_gameRootAbsolutePath(GetAbsoluteRootDirectoryPath())
  41. {
  42. }
  43. State validate(QString& input, [[maybe_unused]] int& pos) const override
  44. {
  45. m_toolTip = "";
  46. if (input.isEmpty())
  47. {
  48. return QValidator::Acceptable;
  49. }
  50. // The underlying file system code can't cope with long file paths, regardless of platform
  51. if (input.length() > (AZ_MAX_PATH_LEN - 1))
  52. {
  53. m_toolTip = QString(g_toolTipInvalidLength).arg(AZ_MAX_PATH_LEN);
  54. return QValidator::Intermediate;
  55. }
  56. QString normalizedInput = QDir::fromNativeSeparators(input);
  57. // Note: the check for the root directory is case insensitive.
  58. // We check if the directory actually exists after this, and at that point,
  59. // if the file path has to be case sensitive, the directory won't exist anyways
  60. // and QFileInfo::exists() will tell us so this should still work even on
  61. // case sensitive file systems (such as Mac and Linux)
  62. if (!normalizedInput.startsWith(m_gameRootAbsolutePath, Qt::CaseInsensitive))
  63. {
  64. m_toolTip = QString(g_toolTipInvalidRoot).arg(m_gameRootAbsolutePath);
  65. return QValidator::Intermediate;
  66. }
  67. QFileInfo fileInfo(normalizedInput);
  68. if (!fileInfo.exists())
  69. {
  70. m_toolTip = g_toolTipPathMustExist;
  71. return QValidator::Intermediate;
  72. }
  73. if (!fileInfo.isDir())
  74. {
  75. m_toolTip = g_toolTipPathMustBeDirectory;
  76. return QValidator::Intermediate;
  77. }
  78. return QValidator::Acceptable;
  79. }
  80. QString infoToolTip() const
  81. {
  82. return m_toolTip;
  83. }
  84. private:
  85. QString m_gameRootAbsolutePath;
  86. mutable QString m_toolTip;
  87. };
  88. SelectDestinationDialog::SelectDestinationDialog(QString message, QWidget* parent, QString suggestedDestination)
  89. : QDialog(parent)
  90. , m_ui(new Ui::SelectDestinationDialog)
  91. , m_validator(new DestinationDialogValidator(this))
  92. {
  93. m_ui->setupUi(this);
  94. QString radioButtonMessage = QString(g_copyFilesMessage).arg(g_assetProcessorLink);
  95. m_ui->RaidoButtonMessage->setText(radioButtonMessage);
  96. SetPreviousOrSuggestedDestinationDirectory(suggestedDestination);
  97. m_ui->DestinationLineEdit->lineEdit()->setValidator(m_validator);
  98. m_ui->DestinationLineEdit->setClearButtonEnabled(true);
  99. connect(m_ui->DestinationLineEdit->lineEdit(), &QLineEdit::textChanged, this, &SelectDestinationDialog::ValidatePath);
  100. connect(m_ui->DestinationLineEdit, &AzQtComponents::BrowseEdit::attachedButtonTriggered, this, &SelectDestinationDialog::OnBrowseDestinationFilePath);
  101. UpdateMessage(message);
  102. InitializeButtons();
  103. }
  104. SelectDestinationDialog::~SelectDestinationDialog()
  105. {
  106. }
  107. void SelectDestinationDialog::InitializeButtons()
  108. {
  109. m_ui->CopyFileRadioButton->setChecked(true);
  110. m_ui->buttonBox->setContentsMargins(0, 0, 16, 16);
  111. QPushButton* importButton = m_ui->buttonBox->addButton(tr("Import"), QDialogButtonBox::AcceptRole);
  112. QPushButton* cancelButton = m_ui->buttonBox->addButton(QDialogButtonBox::Cancel);
  113. importButton->setProperty("class", "Primary");
  114. importButton->setDefault(true);
  115. cancelButton->setProperty("class", "AssetImporterButton");
  116. cancelButton->style()->unpolish(cancelButton);
  117. cancelButton->style()->polish(cancelButton);
  118. cancelButton->update();
  119. connect(importButton, &QPushButton::clicked, this, &SelectDestinationDialog::accept);
  120. connect(cancelButton, &QPushButton::clicked, this, &SelectDestinationDialog::reject);
  121. connect(this, &SelectDestinationDialog::UpdateImportButtonState, importButton, &QPushButton::setEnabled);
  122. // To make sure the import button state is up to date
  123. ValidatePath();
  124. importButton->setAutoDefault(true);
  125. }
  126. void SelectDestinationDialog::SetPreviousOrSuggestedDestinationDirectory(QString suggestedDestination)
  127. {
  128. QString gameRootAbsPath = GetAbsoluteRootDirectoryPath();
  129. QSettings settings;
  130. QString previousDestination = settings.value(g_selectDestinationFilesPath).toString();
  131. // Case 1: if currentDestination is empty at this point, that means this is the first time
  132. // users using the Asset Importer, set the default directory to be the current game project's root folder
  133. // Case 2: if the current folder directory stored in the registry doesn't exist anymore,
  134. // that means users have removed the directory already (deleted or use the Move feature).
  135. // Case 3: if it's a directory outside of the game root folder, then in general,
  136. // users have modified the folder directory in the registry. It should not be happening.
  137. if (previousDestination.isEmpty() || !QDir(previousDestination).exists() || !previousDestination.startsWith(gameRootAbsPath, Qt::CaseInsensitive))
  138. {
  139. previousDestination = gameRootAbsPath;
  140. }
  141. if (!suggestedDestination.isEmpty())
  142. {
  143. previousDestination = suggestedDestination;
  144. }
  145. m_ui->DestinationLineEdit->setText(QDir::toNativeSeparators(previousDestination));
  146. }
  147. void SelectDestinationDialog::accept()
  148. {
  149. QDialog::accept();
  150. // This prevent users from not editing the destination line edit (manually type the directory or browse for the directory)
  151. Q_EMIT SetDestinationDirectory(DestinationDirectory());
  152. if (m_ui->CopyFileRadioButton->isChecked())
  153. {
  154. Q_EMIT DoCopyFiles();
  155. }
  156. else if (m_ui->MoveFileRadioButton->isChecked())
  157. {
  158. Q_EMIT DoMoveFiles();
  159. }
  160. }
  161. void SelectDestinationDialog::reject()
  162. {
  163. Q_EMIT Cancel();
  164. QDialog::reject();
  165. }
  166. void SelectDestinationDialog::ShowMessage()
  167. {
  168. QString message = m_ui->CopyFileRadioButton->isChecked() ? g_copyFilesMessage : g_moveFilesMessage;
  169. m_ui->RaidoButtonMessage->setText(message.arg(g_assetProcessorLink));
  170. }
  171. void SelectDestinationDialog::OnBrowseDestinationFilePath()
  172. {
  173. Q_EMIT BrowseDestinationPath(m_ui->DestinationLineEdit->lineEdit());
  174. }
  175. void SelectDestinationDialog::UpdateMessage(QString message)
  176. {
  177. setWindowTitle(message);
  178. }
  179. void SelectDestinationDialog::ValidatePath()
  180. {
  181. if (!m_ui->DestinationLineEdit->hasAcceptableInput())
  182. {
  183. m_ui->DestinationLineEdit->setToolTip(m_validator->infoToolTip());
  184. Q_EMIT UpdateImportButtonState(false);
  185. }
  186. else
  187. {
  188. QString destinationDirectory = DestinationDirectory();
  189. int strLength = destinationDirectory.length();
  190. // store the updated acceptable destination directory into the registry,
  191. // so that when users manually modify the directory,
  192. // the Asset Importer will remember it
  193. Q_EMIT SetDestinationDirectory(destinationDirectory);
  194. m_ui->DestinationLineEdit->setToolTip("");
  195. Q_EMIT UpdateImportButtonState(strLength > 0);
  196. }
  197. }
  198. QString SelectDestinationDialog::DestinationDirectory() const
  199. {
  200. return QDir::fromNativeSeparators(m_ui->DestinationLineEdit->text());
  201. }
  202. #include <AssetImporter/UI/moc_SelectDestinationDialog.cpp>