serverfilterdock.cpp
1 //------------------------------------------------------------------------------
2 // serverfilterdock.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 // 02110-1301 USA
19 //
20 //------------------------------------------------------------------------------
21 // Copyright (C) 2011 "Zalewa" <zalewapl@gmail.com>
22 //------------------------------------------------------------------------------
23 #include "serverfilterdock.h"
24 #include "ui_serverfilterdock.h"
25 
26 #include "configuration/doomseekerconfig.h"
27 #include "gui/commongui.h"
28 #include "gui/entity/serverlistfilterinfo.h"
29 #include "gui/icons.h"
30 #include "strings.hpp"
31 
32 #include <QAction>
33 #include <QMessageBox>
34 #include <QTimer>
35 
36 DClass<ServerFilterDock> : public Ui::ServerFilterDock
37 {
38 public:
39  static const int CUSTOM_PRESET_IDX = 0;
40 
48  QLineEdit *leQuickSearch;
53  bool bDisableUpdate;
54 
55  void clearCustomPresets()
56  {
57  cboServerFilterPresets->blockSignals(true);
58  for (int i = cboServerFilterPresets->count() - 1;
59  i > CUSTOM_PRESET_IDX;
60  --i)
61  {
62  cboServerFilterPresets->removeItem(i);
63  }
64  cboServerFilterPresets->blockSignals(false);
65  }
66 
67  bool isCustomPreset(const ServerListFilterInfo &preset) const
68  {
69  return isCustomPresetName(preset.name);
70  }
71 
72  bool isCustomPresetName(const QString &name) const
73  {
74  return name.isEmpty();
75  }
76 
77  bool isCustomPresetIndex(int index) const
78  {
79  return index == CUSTOM_PRESET_IDX;
80  }
81 
82  bool isCustomPresetSelected() const
83  {
84  return isCustomPresetIndex(cboServerFilterPresets->currentIndex());
85  }
86 
87  ServerListFilterInfo presetAtIndex(int index) const
88  {
89  return ServerListFilterInfo::deserialize(cboServerFilterPresets->itemData(index));
90  }
91 
92  ServerListFilterInfo currentPreset() const
93  {
94  return presetAtIndex(cboServerFilterPresets->currentIndex());
95  }
96 
97  QString presetNameAtIndex(int index) const
98  {
99  return isCustomPresetIndex(index) ?
100  QString() : presetAtIndex(index).name;
101  }
102 
103  int presetIndex(const QString &name) const
104  {
105  if (isCustomPresetName(name))
106  return CUSTOM_PRESET_IDX;
107  for (int i = CUSTOM_PRESET_IDX + 1; i < cboServerFilterPresets->count(); ++i)
108  {
109  if (presetAtIndex(i).name.compare(name, Qt::CaseInsensitive) == 0)
110  {
111  return i;
112  }
113  }
114  return -1;
115  }
116 
117  QString currentPresetName() const
118  {
119  return presetNameAtIndex(cboServerFilterPresets->currentIndex());
120  }
121 };
122 
123 DPointeredNoCopy(ServerFilterDock)
124 
125 ServerFilterDock::ServerFilterDock(QWidget *pParent)
126  : QDockWidget(pParent)
127 {
128  d->setupUi(this);
129  d->leQuickSearch = nullptr;
130  d->bDisableUpdate = false;
131 
132  d->btnClear->setIcon(Icons::clear());
133  toggleViewAction()->setIcon(QIcon(":/icons/filter.png"));
134 
135  toggleViewAction()->setText(ServerFilterDock::tr("Server &filter"));
136  toggleViewAction()->setShortcut(ServerFilterDock::tr("CTRL+F"));
137 
138  QTimer::singleShot(0, this, &ServerFilterDock::loadSettings);
139 }
140 
141 ServerFilterDock::~ServerFilterDock()
142 {
143 }
144 
145 void ServerFilterDock::addGameModeToComboBox(const QString &gameMode)
146 {
147  addSortedNonDuplicate(d->cboGameMode, gameMode.trimmed());
148  addSortedNonDuplicate(d->cboExcludeGameMode, gameMode.trimmed());
149 }
150 
151 void ServerFilterDock::addSortedNonDuplicate(QComboBox *comboBox, const QString &text)
152 {
153  if (comboBox->findText(text, Qt::MatchFixedString) < 0)
154  {
155  // Make sure combobox contents are sorted.
156  for (int i = 0; i < comboBox->count(); ++i)
157  {
158  if (text < comboBox->itemText(i))
159  {
160  comboBox->insertItem(i, text);
161  return;
162  }
163  }
164 
165  // The above routine didn't return.
166  // This item belongs to the end of the list.
167  comboBox->addItem(text);
168  }
169 }
170 
171 void ServerFilterDock::clear()
172 {
173  setCustomPreset(ServerListFilterInfo());
174  d->cboServerFilterPresets->setCurrentIndex(PrivData<ServerFilterDock>::CUSTOM_PRESET_IDX);
176 }
177 
179 {
180  if (d->leQuickSearch == nullptr)
181  {
182  auto qs = new QLineEdit();
183  qs->setText(d->leServerName->text());
184 
185  connect(d->leServerName, SIGNAL(textEdited(const QString&)), qs, SLOT(setText(const QString&)));
186  connect(qs, SIGNAL(textEdited(const QString&)), d->leServerName, SLOT(setText(const QString&)));
187 
188  d->leQuickSearch = qs;
189  }
190 
191  return d->leQuickSearch;
192 }
193 
194 void ServerFilterDock::emitUpdated()
195 {
196  if (d->bDisableUpdate)
197  return;
198 
199  ServerListFilterInfo filterInfo = this->filterInfo();
200  if (d->isCustomPresetSelected())
201  {
202  setCustomPreset(filterInfo);
203  }
204  else
205  {
206  const ServerListFilterInfo currentPreset = d->currentPreset();
207  d->cboServerFilterPresets->setItemText(d->cboServerFilterPresets->currentIndex(),
208  currentPreset.isFilteringEquivalent(filterInfo) ?
209  currentPreset.name :
210  QString("%1 *").arg(currentPreset.name));
211  }
212 
213  emit filterUpdated(filterInfo);
214 }
215 
216 void ServerFilterDock::enableFilter()
217 {
218  d->cbFilteringEnabled->setChecked(true);
219  emitUpdated();
220 }
221 
222 ServerListFilterInfo ServerFilterDock::filterInfo() const
223 {
224  ServerListFilterInfo filterInfo;
225 
226  filterInfo.bEnabled = d->cbFilteringEnabled->isChecked();
227  filterInfo.bPopulatedServersOnTop = d->cbGroupServersWithPlayersAtTop->isChecked();
228  filterInfo.bShowEmpty = d->cbShowEmpty->isChecked();
229  filterInfo.bShowFull = d->cbShowFull->isChecked();
230  filterInfo.bShowOnlyValid = d->cbShowOnlyValid->isChecked();
231  filterInfo.bShowBannedServers = d->cbShowBannedServers->isChecked();
232  filterInfo.bShowTooSoonServers = d->cbShowTooSoonServers->isChecked();
233  filterInfo.bShowNotRespondingServers = d->cbShowNotRespondingServers->isChecked();
234  filterInfo.gameModes = d->cboGameMode->selectedItemTexts();
235  filterInfo.gameModesExcluded = d->cboExcludeGameMode->selectedItemTexts();
236  filterInfo.lockedServers = Doomseeker::checkboxTristateToShowMode(d->cbShowLockedServers->checkState());
237  filterInfo.maxPing = d->spinMaxPing->value();
238  filterInfo.testingServers = Doomseeker::checkboxTristateToShowMode(d->cbShowTestingServers->checkState());
239  filterInfo.serverName = d->leServerName->text();
240  filterInfo.wads = d->leWads->text().trimmed().split(",", Qt::SkipEmptyParts);
241  filterInfo.wadsExcluded = d->leExcludeWads->text().trimmed().split(",", Qt::SkipEmptyParts);
242 
243  return filterInfo;
244 }
245 
246 int ServerFilterDock::addPreset(const ServerListFilterInfo &preset)
247 {
248  if (d->isCustomPreset(preset))
249  {
250  setCustomPreset(preset);
252  }
253 
254  int index = d->presetIndex(preset.name);
256  {
257  d->cboServerFilterPresets->setItemData(index, preset.serialize());
258  }
259  else
260  {
262  for (; index < d->cboServerFilterPresets->count(); ++index)
263  {
264  if (d->presetAtIndex(index).name.compare(
265  preset.name, Qt::CaseInsensitive) > 0)
266  {
267  break;
268  }
269  }
270  d->cboServerFilterPresets->insertItem(index, preset.name, preset.serialize());
271  }
272  return index;
273 }
274 
275 void ServerFilterDock::setCustomPreset(const ServerListFilterInfo &preset)
276 {
277  static const int idxCustom = PrivData<ServerFilterDock>::CUSTOM_PRESET_IDX;
278  d->cboServerFilterPresets->setItemText(idxCustom,
279  preset.isFilteringAnything() ? tr("[custom]") : tr("[no filter]"));
280  d->cboServerFilterPresets->setItemData(idxCustom, preset.serialize());
281 }
282 
284 {
285  d->bDisableUpdate = true;
286 
287  d->cbFilteringEnabled->setChecked(filterInfo.bEnabled);
288  d->cbGroupServersWithPlayersAtTop->setChecked(filterInfo.bPopulatedServersOnTop);
289  d->cbShowEmpty->setChecked(filterInfo.bShowEmpty);
290  d->cbShowFull->setChecked(filterInfo.bShowFull);
291  d->cbShowOnlyValid->setChecked(filterInfo.bShowOnlyValid);
292  d->cbShowBannedServers->setChecked(filterInfo.bShowBannedServers);
293  d->cbShowTooSoonServers->setChecked(filterInfo.bShowTooSoonServers);
294  d->cbShowNotRespondingServers->setChecked(filterInfo.bShowNotRespondingServers);
295 
296  for (const QString &gameMode : filterInfo.gameModes)
297  {
298  addGameModeToComboBox(gameMode);
299  }
300  d->cboGameMode->setSelectedTexts(filterInfo.gameModes);
301 
302  for (const QString &gameMode : filterInfo.gameModesExcluded)
303  {
304  addGameModeToComboBox(gameMode);
305  }
306  d->cboExcludeGameMode->setSelectedTexts(filterInfo.gameModesExcluded);
307 
308  d->spinMaxPing->setValue(filterInfo.maxPing);
309  if (d->leQuickSearch != nullptr)
310  d->leQuickSearch->setText(filterInfo.serverName.trimmed());
311 
312  d->leServerName->setText(filterInfo.serverName.trimmed());
313  d->leWads->setText(filterInfo.wads.join(",").trimmed());
314  d->leExcludeWads->setText(filterInfo.wadsExcluded.join(",").trimmed());
315 
316  d->cbShowLockedServers->setCheckState(Doomseeker::showModeToCheckboxState(
317  filterInfo.lockedServers));
318  d->cbShowTestingServers->setCheckState(Doomseeker::showModeToCheckboxState(
319  filterInfo.testingServers));
320 
321  d->bDisableUpdate = false;
322  emitUpdated();
323 }
324 
325 void ServerFilterDock::removeFilterPreset()
326 {
327  const QString name = d->currentPresetName();
328  if (QMessageBox::Yes == QMessageBox::question(this,
329  tr("Doomseeker - Remove filter preset"),
330  tr("Are you sure you wish to remove the filter preset \"%1\"?").arg(name)))
331  {
332  if (!name.isEmpty() && !d->isCustomPresetName(name))
333  {
334  gConfig.serverFilter.presets.remove(name);
335  d->cboServerFilterPresets->removeItem(d->presetIndex(name));
336  }
337  }
338 }
339 
340 void ServerFilterDock::saveFilterAsPreset()
341 {
342  bool ok = false;
343  const QString name = CommonGUI::getText(this, tr("Doomseeker - Save filter preset"),
344  tr("Enter name for the filter preset (will overwrite if exists):"),
345  QLineEdit::Normal, d->currentPresetName(), &ok).trimmed();
346 
347  if (!ok || name.isEmpty())
348  return;
349 
350  ServerListFilterInfo filter = this->filterInfo();
351  filter.name = name;
352  gConfig.serverFilter.presets[filter.name] = filter.serialize();
353  gConfig.saveToFile();
354  const int index = addPreset(filter);
355  d->cboServerFilterPresets->setCurrentIndex(index);
356  d->cboServerFilterPresets->setItemText(index, name); // reset the * if any
357 }
358 
359 void ServerFilterDock::loadSettings()
360 {
361  // Presets.
362  d->clearCustomPresets();
363  for (const QVariant &serialized : gConfig.serverFilter.presets)
364  {
365  ServerListFilterInfo preset = ServerListFilterInfo::deserialize(serialized);
366  if (!preset.name.isEmpty())
367  addPreset(preset);
368  }
369 
370  // The custom filter.
371  setCustomPreset(gConfig.serverFilter.info);
372 
373  // Now select the current filter.
374  int currentPresetIdx = d->presetIndex(gConfig.serverFilter.currentPreset);
375  if (currentPresetIdx < 0 || currentPresetIdx >= d->cboServerFilterPresets->count())
376  {
378  }
379  selectFilterPreset(currentPresetIdx);
380 }
381 
382 void ServerFilterDock::selectFilterPreset(int index)
383 {
384  d->cboServerFilterPresets->blockSignals(true);
385  d->cboServerFilterPresets->setCurrentIndex(index);
386  d->cboServerFilterPresets->blockSignals(false);
387  d->btnRemovePreset->setEnabled(!d->isCustomPresetIndex(index) && index > 0);
388  gConfig.serverFilter.currentPreset = d->currentPresetName();
390  i < d->cboServerFilterPresets->count();
391  ++i)
392  {
393  // Reset any '*'
394  d->cboServerFilterPresets->setItemText(i, d->presetNameAtIndex(i));
395  }
396  setFilterInfo(ServerListFilterInfo::deserialize(
397  d->cboServerFilterPresets->itemData(index)));
398 }