MythTV master
storagegroupeditor.cpp
Go to the documentation of this file.
1// Qt headers
2#include <QCoreApplication>
3#include <QDir>
4#include <QFile>
5#include <QUrl>
6#include <QVector>
7#include <utility>
8
9// MythTV headers
12#include "libmythbase/mythdb.h"
14
15#include "storagegroupeditor.h"
16
17#define LOC QString("SGE(%1): ").arg(m_groupname)
18
19/****************************************************************************/
20
22 m_group(std::move(group))
23{
24 SetLabel();
25}
26
28{
29 QString dispGroup = m_group;
30
31 if (m_group == "Default")
32 dispGroup = tr("Default", "Default storage group");
33 else if (StorageGroup::kSpecialGroups.contains(m_group))
34 dispGroup = QCoreApplication::translate("(StorageGroups)",
35 m_group.toLatin1().constData());
36
38 {
39 setLabel(tr("'%1' Storage Group Directories").arg(dispGroup));
40 }
41 else
42 {
43 setLabel(tr("Local '%1' Storage Group Directories").arg(dispGroup));
44 }
45}
46
48{
49 QStringList actions;
50 bool handled =
51 GetMythMainWindow()->TranslateKeyPress("Global", e, actions);
52 for (int i = 0; i < actions.size() && !handled; i++)
53 {
54 const QString& action = actions[i];
55
56 if (action == "DELETE")
57 {
58 handled = true;
60 }
61 }
62 if (handled)
63 return handled;
65}
66
68{
69 return true;
70}
71
73{
74 bool is_master_host = gCoreContext->IsMasterHost();
75
76 QString dispGroup = m_group;
77 if (m_group == "Default")
78 dispGroup = tr("Default", "Default storage group");
79 else if (StorageGroup::kSpecialGroups.contains(m_group))
80 dispGroup = QCoreApplication::translate("(StorageGroups)",
81 m_group.toLatin1().constData());
82
83 QString message = tr("Delete '%1' Storage Group?").arg(dispGroup);
84 if (is_master_host)
85 {
86 if (m_group == "Default")
87 {
88 message = tr("Delete '%1' Storage Group?\n(from remote hosts)").arg(dispGroup);
89 }
90 else
91 {
92 message = tr("Delete '%1' Storage Group?\n(from all hosts)").arg(dispGroup);
93 }
94 }
95
96 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
97 auto *confirmDelete = new MythConfirmationDialog(popupStack, message, true);
98 if (confirmDelete->Create())
99 {
100 connect(confirmDelete, &MythConfirmationDialog::haveResult,
102 popupStack->AddScreen(confirmDelete);
103 }
104 else
105 {
106 delete confirmDelete;
107 }
108}
109
111{
112 if (doDelete)
113 {
114 bool is_master_host = gCoreContext->IsMasterHost();
116 QString sql = "DELETE FROM storagegroup "
117 "WHERE groupname = :NAME";
118 if (is_master_host)
119 {
120 // From the master host, delete the group completely (versus just
121 // local directory list) unless it's the Default group, then just
122 // delete remote overrides of the Default group
123 if (m_group == "Default")
124 sql.append(" AND hostname != :HOSTNAME");
125 }
126 else
127 {
128 // For non-master hosts, delete only the local override of the
129 // group directory list
130 sql.append(" AND hostname = :HOSTNAME");
131 }
132 sql.append(';');
133 query.prepare(sql);
134 query.bindValue(":NAME", m_group);
135 if (!is_master_host || (m_group == "Default"))
136 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
137 if (!query.exec())
138 MythDB::DBError("StorageGroupListEditor::doDelete", query);
139 }
140}
141
143 int id,
144 QString group) :
145 SimpleDBStorage(_user, "storagegroup", "dirname"),
146 m_id(id),
147 m_group(std::move(group))
148{
149}
150
152{
153 QString dirnameTag(":SETDIRNAME");
154
155 QString query("dirname = " + dirnameTag);
156
157 bindings.insert(dirnameTag, m_user->GetDBValue());
158
159 return query;
160}
161
163{
164 QString hostnameTag(":WHEREHOST");
165 QString idTag(":WHEREID");
166
167 QString query("hostname = " + hostnameTag + " AND id = " + idTag);
168
169 bindings.insert(hostnameTag, gCoreContext->GetHostName());
170 bindings.insert(idTag, m_id);
171
172 return query;
173}
174
177 m_id(id), m_group(group)
178{
179 SetTypeFilter(QDir::AllDirs | QDir::Drives);
180}
181
183{
184 delete GetStorage();
185}
186
188{
189 QStringList actions;
190 bool handled =
191 GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
192 for (int i = 0; i < actions.size() && !handled; i++)
193 {
194 const QString& action = actions[i];
195
196 if (action == "DELETE")
197 {
198 handled = true;
200 }
201 }
202 return handled;
203}
204
206{
207 QString message =
208 tr("Remove '%1'\nDirectory From Storage Group?").arg(getValue());
209 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
210 auto *confirmDelete = new MythConfirmationDialog(popupStack, message, true);
211
212 if (confirmDelete->Create())
213 {
214 connect(confirmDelete, &MythConfirmationDialog::haveResult,
216 popupStack->AddScreen(confirmDelete);
217 }
218 else
219 {
220 delete confirmDelete;
221 }
222}
223
225{
226 if (doDelete)
227 {
229 query.prepare("DELETE FROM storagegroup "
230 "WHERE groupname = :NAME "
231 "AND dirname = :DIRNAME "
232 "AND hostname = :HOSTNAME;");
233 query.bindValue(":NAME", m_group);
234 query.bindValue(":DIRNAME", getValue());
235 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
236 if (query.exec())
237 getParent()->removeChild(this);
238 else
239 MythDB::DBError("StorageGroupEditor::DoDeleteSlot", query);
240 }
241}
242
244{
246
247 auto *button = new ButtonStandardSetting(tr("(Add New Directory)"));
249 addChild(button);
250
252 query.prepare("SELECT dirname, id FROM storagegroup "
253 "WHERE groupname = :NAME AND hostname = :HOSTNAME "
254 "ORDER BY id;");
255 query.bindValue(":NAME", m_group);
256 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
257 if (!query.exec() || !query.isActive())
258 {
259 MythDB::DBError("StorageGroupEditor::Load", query);
260 }
261 else
262 {
263 bool first = true;
264 QString dirname;
265 while (query.next())
266 {
267 /* The storagegroup.dirname column uses utf8_bin collation, so Qt
268 * uses QString::fromAscii() for toString(). Explicitly convert the
269 * value using QString::fromUtf8() to prevent corruption. */
270 dirname = QString::fromUtf8(query.value(0)
271 .toByteArray().constData());
272 if (first)
273 {
274 first = false;
275 }
276 addChild(new StorageGroupDirSetting(query.value(1).toInt(),
277 m_group));
278 }
279
280 if (!first)
281 {
282 SetLabel();
283 }
284 }
285
287}
288
290{
291 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
292
293 auto *settingdialog = new MythUIFileBrowser(popupStack, "");
294 settingdialog->SetTypeFilter(QDir::AllDirs | QDir::Drives);
295
296 if (settingdialog->Create())
297 {
298 settingdialog->SetReturnEvent(this, "editsetting");
299 popupStack->AddScreen(settingdialog);
300 }
301 else
302 {
303 delete settingdialog;
304 }
305}
306
308{
309 if (event->type() == DialogCompletionEvent::kEventType)
310 {
311 auto *dce = dynamic_cast<DialogCompletionEvent*>(event);
312 if (dce == nullptr)
313 return;
314 QString resultid = dce->GetId();
315
316 if (resultid == "editsetting")
317 {
318 QString dirname = dce->GetResultText();
319
320 if (dirname.isEmpty())
321 return;
322
323 if (!dirname.endsWith("/"))
324 dirname.append("/");
325
327 query.prepare("INSERT INTO storagegroup (groupname, hostname, dirname) "
328 "VALUES (:NAME, :HOSTNAME, :DIRNAME);");
329 query.bindValue(":NAME", m_group);
330 query.bindValue(":DIRNAME", dirname);
331 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
332 if (!query.exec())
333 {
334 MythDB::DBError("StorageGroupEditor::customEvent", query);
335 }
336 else
337 {
338 SetLabel();
339 StandardSetting *directory =
340 new StorageGroupDirSetting(query.lastInsertId().toInt(),
341 m_group);
342 directory->setValue(dirname);
343 addChild(directory);
344 emit settingsChanged(this);
345 }
346 }
347 }
348}
349
350
351/****************************************************************************/
352
354{
356 setLabel(tr("Storage Groups (directories for new recordings)"));
357 else
358 setLabel(tr("Local Storage Groups (directories for new recordings)"));
359}
360
361void StorageGroupListEditor::AddSelection(const QString &label,
362 const QString &value)
363{
364 auto *button = new StorageGroupEditor(value);
365 button->setLabel(label);
366 addChild(button);
367}
368
370{
371 QStringList names;
372 QStringList masterNames;
373 bool createAddDefaultButton = false;
374 QVector< bool > createAddSpecialGroupButton( StorageGroup::kSpecialGroups.size() );
375 bool isMaster = gCoreContext->IsMasterHost();
376
378 query.prepare("SELECT distinct groupname "
379 "FROM storagegroup "
380 "WHERE hostname = :HOSTNAME "
381 "ORDER BY groupname;");
382 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
383 if (!query.exec())
384 {
385 MythDB::DBError("StorageGroup::Load getting local group names",
386 query);
387 }
388 else
389 {
390 while (query.next())
391 names << query.value(0).toString();
392 }
393
394 query.prepare("SELECT distinct groupname "
395 "FROM storagegroup "
396 "ORDER BY groupname;");
397 if (!query.exec())
398 {
399 MythDB::DBError("StorageGroup::Load getting all group names",
400 query);
401 }
402 else
403 {
404 while (query.next())
405 masterNames << query.value(0).toString();
406 }
407
409
410 if (isMaster || names.contains("Default"))
411 {
412 AddSelection(tr("Default", "Default storage group"),
413 "Default");
414 }
415 else
416 {
417 createAddDefaultButton = true;
418 }
419
420 int curGroup = 0;
421 QString groupName;
422 while (curGroup < StorageGroup::kSpecialGroups.size())
423 {
424 groupName = StorageGroup::kSpecialGroups[curGroup];
425 if (names.contains(groupName))
426 {
427 addChild(new StorageGroupEditor(groupName));
428 createAddSpecialGroupButton[curGroup] = false;
429 }
430 else
431 {
432 createAddSpecialGroupButton[curGroup] = true;
433 }
434 curGroup++;
435 }
436
437 int curName = 0;
438 while (curName < names.size())
439 {
440 if ((names[curName] != "Default") &&
441 (!StorageGroup::kSpecialGroups.contains(names[curName])))
442 addChild(new StorageGroupEditor(names[curName]));
443 curName++;
444 }
445
446 if (createAddDefaultButton)
447 {
448 AddSelection(tr("(Create default group)"), "Default");
449 }
450
451 curGroup = 0;
452 while (curGroup < StorageGroup::kSpecialGroups.size())
453 {
454 groupName = StorageGroup::kSpecialGroups[curGroup];
455 if (createAddSpecialGroupButton[curGroup])
456 {
457 AddSelection(tr("(Create %1 group)")
458 .arg(QCoreApplication::translate("(StorageGroups)",
459 groupName.toLatin1().constData())),
460 groupName);
461 }
462 curGroup++;
463 }
464
465 if (isMaster)
466 {
467 auto *newGroup = new ButtonStandardSetting(tr("(Create new group)"));
469 addChild(newGroup);
470 }
471 else
472 {
473 curName = 0;
474 while (curName < masterNames.size())
475 {
476 if ((masterNames[curName] != "Default") &&
477 (!StorageGroup::kSpecialGroups.contains(masterNames[curName])) &&
478 (!names.contains(masterNames[curName])))
479 {
480 AddSelection(tr("(Create %1 group)")
481 .arg(masterNames[curName]),
482 masterNames[curName]);
483 }
484 curName++;
485 }
486 }
487
489}
490
491
493{
494 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
495 auto *settingdialog = new MythTextInputDialog(popupStack,
496 tr("Enter the name of the new storage group"));
497
498 if (settingdialog->Create())
499 {
500 connect(settingdialog, &MythTextInputDialog::haveResult,
502 popupStack->AddScreen(settingdialog);
503 }
504 else
505 {
506 delete settingdialog;
507 }
508}
509
511{
512 auto *button = new StorageGroupEditor(name);
513 button->setLabel(name + tr(" Storage Group Directories"));
514 button->Load();
515 addChild(button);
516 emit settingsChanged(this);
517}
518
519#include "moc_storagegroupeditor.cpp"
StorageUser * m_user
Definition: mythstorage.h:50
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:40
static const Type kEventType
Definition: mythdialogbox.h:55
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:129
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:839
QVariant value(int i) const
Definition: mythdbcon.h:205
bool isActive(void) const
Definition: mythdbcon.h:216
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:620
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:890
QVariant lastInsertId()
Return the id of the last inserted row.
Definition: mythdbcon.cpp:937
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:814
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:552
Dialog asking for user confirmation.
QString GetHostName(void)
bool IsMasterHost(void)
is this the same host as the master
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:225
bool TranslateKeyPress(const QString &Context, QKeyEvent *Event, QStringList &Actions, bool AllowJumps=true)
Get a list of actions for a keypress in the given context.
MythScreenStack * GetStack(const QString &Stackname)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Dialog prompting the user to enter a text string.
void haveResult(QString)
void SetTypeFilter(QDir::Filters filter)
virtual void addChild(StandardSetting *child)
virtual void Load(void)
virtual void clearSettings()
void settingsChanged(StandardSetting *selectedSetting=nullptr)
StandardSetting * getParent() const
virtual void removeChild(StandardSetting *child)
virtual void setValue(const QString &newValue)
Storage * GetStorage(void) const
virtual bool keyPressEvent(QKeyEvent *event)
virtual QString getValue(void) const
virtual void setLabel(QString str)
StorageGroupDirSetting(int id, const QString &group)
void DoDeleteSlot(bool doDelete)
bool keyPressEvent(QKeyEvent *event) override
StorageGroupDirStorage(StorageUser *_user, int id, QString group)
QString GetWhereClause(MSqlBindings &bindings) const override
QString GetSetClause(MSqlBindings &bindings) const override
void customEvent(QEvent *event) override
void Load(void) override
StorageGroupEditor(QString group)
void DoDeleteSlot(bool doDelete)
bool keyPressEvent(QKeyEvent *event) override
bool canDelete(void) override
void Load(void) override
void CreateNewGroup(const QString &name)
void ShowNewGroupDialog(void) const
void AddSelection(const QString &label, const QString &value)
static const QStringList kSpecialGroups
Definition: storagegroup.h:46
virtual QString GetDBValue(void) const =0
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QMap< QString, QVariant > MSqlBindings
typedef for a map of string -> string bindings for generic queries.
Definition: mythdbcon.h:101
MythMainWindow * GetMythMainWindow(void)