23 #include "cfgfilepaths.h"
25 #include "configuration/doomseekerconfig.h"
26 #include "datapaths.h"
27 #include "pathfinder/filesearchpath.h"
28 #include "templatedpathresolver.h"
29 #include "ui_cfgfilepaths.h"
30 #include <QApplication>
31 #include <QFileDialog>
34 static const int COL_PATH = 0;
35 static const int COL_RECURSE = 1;
36 static const int NUM_COLS = 2;
38 DClass<CFGFilePaths> :
public Ui::CFGFilePaths
43 bool bResolveTemplatedPathsPlaceholders;
46 void resetSavedState()
48 savedState.bResolveTemplatedPathsPlaceholders = gConfig.doomseeker.bResolveTemplatedPathsPlaceholders;
57 set_reject(&CFGFilePaths::reject_);
61 labels << CFGFilePaths::tr(
"Path") << CFGFilePaths::tr(
"Recurse");
62 d->tblFilePaths->setColumnCount(NUM_COLS);
63 d->tblFilePaths->setHorizontalHeaderLabels(labels);
65 QHeaderView *header = d->tblFilePaths->horizontalHeader();
66 header->setSectionResizeMode(COL_PATH, QHeaderView::Stretch);
67 header->setSectionResizeMode(COL_RECURSE, QHeaderView::ResizeToContents);
69 QString templatedPathsToolTip = QString(
"<p>%1</p>").arg(CFGFilePaths::tr(
70 "Doomseeker supports special characters in user-configurable "
71 "paths. This affects all paths everywhere, not only the file "
72 "paths configured on this page. The placeholders are:"));
73 templatedPathsToolTip +=
"<ul>";
75 templatedPathsToolTip += QString(
"<li>%1</li>").arg(CFGFilePaths::tr(
76 "<b>~</b> - if at the beginning of the path, resolved to "
77 "the home directory of the current user"));
78 #ifdef DOOMSEEKER_TEMPLATED_PATH_RESOLVER_TILDEUSER
79 templatedPathsToolTip += QString(
"<li>%1</li>").arg(CFGFilePaths::tr(
80 "<b>~<i>user</i></b> - if at the beginning of the path, resolved to "
81 "the home directory of the specified <i>user</i>"));
83 templatedPathsToolTip += QString(
"<li>%1</li>").arg(CFGFilePaths::tr(
84 "<b>$PROGDIR</b> - resolved to the directory where Doomseeker's "
85 "executable is located; this may create invalid paths if "
86 "used in the middle of the path"));
87 templatedPathsToolTip += QString(
"<li>%1</li>").arg(CFGFilePaths::tr(
88 "<b>$<i>NAME</i></b> - any <i>NAME</i> is resolved to the environment "
89 "variable of the same <i>NAME</i>, or to empty if the environment "
90 "variable is absent"));
91 templatedPathsToolTip +=
"</ul>";
93 templatedPathsToolTip += QString(
"<p>%1</p>").arg(CFGFilePaths::tr(
94 "This option controls whether the <b>$PROGDIR</b> "
95 "and <b>$<i>NAME</i></b> will be resolved or ignored. "
96 "If this option is <b>checked</b>, both are resolved as explained. "
97 "If this option is <b>unchecked</b>, both will be taken literally. "
98 "The <b>~</b> placeholders are always resolved regardless."));
100 d->cbResolveTemplatedPathsPlaceholders->setToolTip(templatedPathsToolTip);
101 d->btnBrowseWadPath->setIcon(style()->standardIcon(QStyle::SP_DirOpenIcon));
103 connect(d->btnAddWadPath, SIGNAL(clicked()),
this, SLOT(btnAddWadPath_Click()));
104 connect(d->btnBrowseWadPath, &QPushButton::clicked,
this, &CFGFilePaths::btnBrowseWadPath_Click);
105 connect(d->btnRemoveWadPath, SIGNAL(clicked()),
this, SLOT(btnRemoveWadPath_Click()));
106 this->connect(d->tblFilePaths->itemDelegate(),
107 SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)),
108 SIGNAL(validationRequested()));
109 connect(d->cbResolveTemplatedPathsPlaceholders, &QAbstractButton::toggled,
110 this, &CFGFilePaths::cbResolveTemplatedPathsPlaceholders_Toggled);
111 d->tblFilePaths->connect(d->tblFilePaths,
112 &TableWidgetReorderable::rowsReordered,
113 &QTableWidget::resizeRowsToContents);
116 CFGFilePaths::~CFGFilePaths()
122 if (fileSearchPath.isValid() && !isPathAlreadyDefined(fileSearchPath.path()))
123 addPathToTable(fileSearchPath);
126 void CFGFilePaths::addPathToTable(
const FileSearchPath &fileSearchPath)
128 d->tblFilePaths->setSortingEnabled(
false);
129 int newRow = d->tblFilePaths->rowCount();
130 d->tblFilePaths->insertRow(newRow);
131 auto path =
new QTableWidgetItem(fileSearchPath.path());
132 path->setData(Qt::ToolTipRole, fileSearchPath.path());
133 auto recurse =
new QTableWidgetItem();
134 recurse->setCheckState(fileSearchPath.isRecursive() ? Qt::Checked : Qt::Unchecked);
135 recurse->setData(Qt::TextAlignmentRole, Qt::AlignCenter);
137 d->tblFilePaths->setItem(newRow, COL_PATH, path);
138 d->tblFilePaths->setItem(newRow, COL_RECURSE, recurse);
139 d->tblFilePaths->resizeRowsToContents();
140 d->tblFilePaths->setSortingEnabled(
true);
143 void CFGFilePaths::btnAddWadPath_Click()
145 addPathToTable(QString());
149 void CFGFilePaths::btnBrowseWadPath_Click()
151 QString strDir = QFileDialog::getExistingDirectory(
this, tr(
"Doomseeker - Add game mod path"));
152 addPath(gDefaultDataPaths->portablizePath(strDir));
156 void CFGFilePaths::btnRemoveWadPath_Click()
158 QSet<int> uniqueRows;
159 for (
auto *item : d->tblFilePaths->selectedItems())
160 uniqueRows.insert(item->row());
161 QList<int> rows = uniqueRows.values();
163 std::sort(rows.begin(), rows.end(), [](
int a,
int b) {
return b < a; });
165 d->tblFilePaths->removeRow(row);
169 void CFGFilePaths::cbResolveTemplatedPathsPlaceholders_Toggled(
bool state)
171 gConfig.doomseeker.bResolveTemplatedPathsPlaceholders = state;
177 return style()->standardIcon(QStyle::SP_DirOpenIcon);
180 bool CFGFilePaths::isPathAlreadyDefined(
const QString &path)
const
182 Qt::CaseSensitivity caseSensitivity;
185 caseSensitivity = Qt::CaseInsensitive;
187 caseSensitivity = Qt::CaseSensitive;
190 for (
int i = 0; i < d->tblFilePaths->rowCount(); ++i)
192 const QTableWidgetItem *item = d->tblFilePaths->item(i, COL_PATH);
193 QString dir = item->text();
195 if (dir.compare(path, caseSensitivity) == 0)
204 d->resetSavedState();
206 const QList<FileSearchPath> &wadPaths = gConfig.doomseeker.wadPaths;
207 for (
int i = 0; i < wadPaths.count(); ++i)
208 addPath(wadPaths[i]);
210 d->cbTellMeWhereAreMyWads->setChecked(gConfig.doomseeker.bTellMeWhereAreTheWADsWhenIHoverCursorOverWADSColumn);
211 d->cbCheckTheIntegrityOfWads->setChecked(gConfig.doomseeker.bCheckTheIntegrityOfWads);
212 d->cbResolveTemplatedPathsPlaceholders->setChecked(gConfig.doomseeker.bResolveTemplatedPathsPlaceholders);
217 QList<FileSearchPath> wadPaths;
219 for (
int i = 0; i < d->tblFilePaths->rowCount(); ++i)
221 QTableWidgetItem *itemPath = d->tblFilePaths->item(i, COL_PATH);
222 QTableWidgetItem *itemRecurse = d->tblFilePaths->item(i, COL_RECURSE);
224 fileSearchPath.setRecursive(itemRecurse->checkState() == Qt::Checked);
225 wadPaths << fileSearchPath;
228 gConfig.doomseeker.wadPaths = wadPaths;
229 gConfig.doomseeker.bTellMeWhereAreTheWADsWhenIHoverCursorOverWADSColumn = d->cbTellMeWhereAreMyWads->isChecked();
230 gConfig.doomseeker.bCheckTheIntegrityOfWads = d->cbCheckTheIntegrityOfWads->isChecked();
231 gConfig.doomseeker.bResolveTemplatedPathsPlaceholders = d->cbResolveTemplatedPathsPlaceholders->isChecked();
233 d->resetSavedState();
236 void CFGFilePaths::reject_()
238 gConfig.doomseeker.bResolveTemplatedPathsPlaceholders = d->savedState.bResolveTemplatedPathsPlaceholders;
243 bool allPathsValid =
true;
244 for (
int i = 0; i < d->tblFilePaths->rowCount(); ++i)
246 QTableWidgetItem *itemPath = d->tblFilePaths->item(i, COL_PATH);
249 bool valid = validationError.isEmpty();
250 allPathsValid = allPathsValid && valid;
252 itemPath->setIcon(valid ? QIcon() : QIcon(
":/icons/exclamation_16.png"));
253 itemPath->setToolTip(validationError);
258 QString CFGFilePaths::validatePath(
const QString &path)
const
260 if (path.trimmed().isEmpty())
261 return tr(
"Path empty.");
263 QFileInfo fileInfo(path.trimmed());
264 if (!fileInfo.exists())
265 return tr(
"Path doesn't exist.");
267 if (!fileInfo.isDir())
268 return tr(
"Path is not a directory.");