MythTV master
mythcontrols.cpp
Go to the documentation of this file.
1// -*- Mode: c++ -*-
28// Qt headers
29#include <QCoreApplication>
30#include <QStringList>
31
32// MythTV headers
40
41// MythControls headers
42#include "keygrabber.h"
43#include "mythcontrols.h"
44
45#define LOC QString("MythControls: ")
46#define LOC_ERR QString("MythControls, Error: ")
47
49{
50 Teardown();
51}
52
54{
55 if (m_bindings)
56 {
57 delete m_bindings;
58 m_bindings = nullptr;
59 }
60
61 m_contexts.clear();
62}
63
70{
71 // Load the theme for this screen
72 bool foundtheme = LoadWindowFromXML("controls-ui.xml", "controls", this);
73 if (!foundtheme)
74 return false;
75
76 m_description = dynamic_cast<MythUIText *>(GetChild("description"));
77 m_leftList = dynamic_cast<MythUIButtonList *>(GetChild("leftlist"));
78 m_rightList = dynamic_cast<MythUIButtonList *>(GetChild("rightlist"));
79 m_leftDescription = dynamic_cast<MythUIText *>(GetChild("leftdesc"));
80 m_rightDescription = dynamic_cast<MythUIText *>(GetChild("rightdesc"));
81
84 {
85 LOG(VB_GENERAL, LOG_ERR, "Theme is missing critical theme elements.");
86 return false;
87 }
88
93
100
101 for (uint i = 0; i < Action::kMaximumNumberOfBindings; i++)
102 {
103 MythUIButton *button = dynamic_cast<MythUIButton *>
104 (GetChild(QString("action_%1").arg(i)));
105
106 if (!button)
107 {
108 LOG(VB_GENERAL, LOG_ERR, LOC +
109 QString("Unable to load action button action_%1").arg(i));
110
111 return false;
112 }
113
115
116 m_actionButtons.append(button);
117 }
118
120
122
123 /* start off with the actions by contexts view */
127
128 return true;
129}
130
137{
139 return;
140
141 if (direction == 0)
143}
144
149{
151}
152
157{
160}
161
166{
167 QString key = GetCurrentKey();
168 if (!key.isEmpty())
169 {
170 QString label = tr("Modify Action");
171
172 MythScreenStack *popupStack =
173 GetMythMainWindow()->GetStack("popup stack");
174
176 new MythDialogBox(label, popupStack, "actionmenu");
177
178 if (m_menuPopup->Create())
179 popupStack->AddScreen(m_menuPopup);
180
181 m_menuPopup->SetReturnEvent(this, "action");
182
183 m_menuPopup->AddButton(tr("Set Binding"));
184 m_menuPopup->AddButton(tr("Remove Binding"));
185 }
186 else {
187 // for blank keys, no reason to ask what to do
188 GrabKey();
189 }
190}
191
196{
197 QString label = tr("Change View");
198
199 MythScreenStack *popupStack =
200 GetMythMainWindow()->GetStack("popup stack");
201
203 new MythDialogBox(label, popupStack, "mcviewmenu");
204
205 if (m_menuPopup->Create())
206 popupStack->AddScreen(m_menuPopup);
207
208 m_menuPopup->SetReturnEvent(this, "view");
209
210 m_menuPopup->AddButton(tr("Actions By Context"));
211 m_menuPopup->AddButton(tr("Contexts By Key"));
212 m_menuPopup->AddButton(tr("Keys By Context"));
213
214}
215
217{
218 QString label = tr("Options");
219
220 MythScreenStack *popupStack =
221 GetMythMainWindow()->GetStack("popup stack");
222
224 new MythDialogBox(label, popupStack, "optionmenu");
225
226 if (m_menuPopup->Create())
227 popupStack->AddScreen(m_menuPopup);
228
229 m_menuPopup->SetReturnEvent(this, "option");
230
231 m_menuPopup->AddButton(tr("Save"));
232 m_menuPopup->AddButton(tr("Change View"));
233 m_menuPopup->AddButton(tr("Reset All Keys to Defaults"));
234}
235
237{
239 {
240 /* prompt user to save changes */
241 QString label = tr("Save changes?");
242
243 MythScreenStack *popupStack =
244 GetMythMainWindow()->GetStack("popup stack");
245
246 auto *confirmPopup = new MythConfirmationDialog(popupStack, label, true);
247
248 if (confirmPopup->Create())
249 popupStack->AddScreen(confirmPopup);
250
251 confirmPopup->SetReturnEvent(this, "exit");
252 }
253 else
254 {
256 }
257}
258
264{
266}
267
273{
275}
276
277
285 MythUIButtonList *uilist, const QStringList &contents, bool arrows)
286{
287 // remove all strings from the current list
288 uilist->Reset();
289
290 // add each new string
291 for (const auto & content : std::as_const(contents))
292 {
293 auto *item = new MythUIButtonListItem(uilist, content);
294 item->setDrawArrow(arrows);
295 }
296}
297
302{
303 // get the selected item in the right list.
305
306 if (!item)
307 return;
308
309 QString rtstr = item->GetText();
310
311 switch(m_currentView)
312 {
315 break;
316 case kKeysByContext:
318 break;
319 case kContextsByKey:
321 break;
322 }
323}
324
330{
331 for (uint i = 0; i < Action::kMaximumNumberOfBindings; i++)
332 m_actionButtons.at(i)->SetText("");
333
334 if (GetFocusWidget() == m_leftList)
335 {
337 return;
338 }
339
340 const QString context = GetCurrentContext();
341 const QString action = GetCurrentAction();
342
343 QString desc = m_bindings->GetActionDescription(context, action);
344 m_description->SetText(tr(desc.toLatin1().constData()));
345
346 QStringList keys = m_bindings->GetActionKeys(context, action);
347 for (int i = 0; (i < keys.count()) &&
348 (i < (int)Action::kMaximumNumberOfBindings); i++)
349 {
350 m_actionButtons.at(i)->SetText(keys[i]);
351 }
352}
353
354
363{
365 return m_leftList->GetItemCurrent()->GetText();
366
367 if (GetFocusWidget() == m_leftList)
368 return {};
369
370 QString desc = m_rightList->GetItemCurrent()->GetText();
371 int loc = desc.indexOf(" => ");
372 if (loc == -1)
373 return {}; // Should not happen
374
376 return desc.left(loc);
377
378 return desc.mid(loc + 4);
379}
380
389{
391 {
393 {
394 return m_leftList->GetItemCurrent()->GetText();
395 }
396 return {};
397 }
398
399 if (GetFocusWidget() == m_leftList)
400 return {};
401
403 return {};
404
405 QString desc = m_rightList->GetItemCurrent()->GetText();
408 {
409 return desc;
410 }
411
412 int loc = desc.indexOf(" => ");
413 if (loc == -1)
414 return {}; // should not happen..
415
417 return desc.left(loc);
418
419 QString rv = desc.mid(loc+4);
420 if (rv == "<none>")
421 return {};
422
423 return rv;
424}
425
431{
432 for (uint i = 0; i < Action::kMaximumNumberOfBindings; i++)
433 {
434 MythUIButton *button = m_actionButtons.at(i);
435 MythUIType *uitype = GetFocusWidget();
436 if (uitype == button)
437 return i;
438 }
439
441}
442
451{
452 MythUIButtonListItem* currentButton = nullptr;
454 {
455 currentButton = m_leftList->GetItemCurrent();
456 if (currentButton != nullptr)
457 return currentButton->GetText();
458 }
459
460 if (GetFocusWidget() == m_leftList)
461 return {};
462
464 {
465 QString context = GetCurrentContext();
466 QString action = GetCurrentAction();
468 QStringList keys = m_bindings->GetActionKeys(context, action);
469
470 if (b < (uint)keys.count())
471 return keys[b];
472
473 return {};
474 }
475
476 currentButton = m_rightList->GetItemCurrent();
477 QString desc;
478 if (currentButton)
479 desc = currentButton->GetText();
480
481 int loc = desc.indexOf(" => ");
482 if (loc == -1)
483 return {}; // Should not happen
484
485
487 return desc.left(loc);
488
489 return desc.mid(loc + 4);
490}
491
497{
498 /* create the key bindings and the tree */
501
502 /* Alphabetic order, but jump and global at the top */
503 m_sortedContexts.sort();
506 m_sortedContexts.insert(m_sortedContexts.begin(),
508 m_sortedContexts.insert(m_sortedContexts.begin(),
510
511 for (const auto & ctx_name : std::as_const(m_sortedContexts))
512 {
513 QStringList actions = m_bindings->GetActions(ctx_name);
514 actions.sort();
515 m_contexts.insert(ctx_name, actions);
516 }
517}
518
526{
527 QString context = GetCurrentContext();
528 QString key = GetCurrentKey();
529 QString action = GetCurrentAction();
530
531 if (context.isEmpty() || key.isEmpty() || action.isEmpty())
532 {
533 LOG(VB_GENERAL, LOG_ERR,
534 "Unable to delete binding, missing information");
535 return;
536 }
537
538 if (m_bindings->RemoveActionKey(context, action, key))
539 {
541 return;
542 }
543
544 QString label = tr("This action is mandatory and needs at least one key "
545 "bound to it. Instead, try rebinding with another key.");
546
547 MythScreenStack *popupStack =
548 GetMythMainWindow()->GetStack("popup stack");
549
550 auto *confirmPopup = new MythConfirmationDialog(popupStack, label, false);
551
552 if (confirmPopup->Create())
553 {
554 confirmPopup->SetReturnEvent(this, "mandatorydelete");
555 popupStack->AddScreen(confirmPopup);
556 }
557 else
558 {
559 delete confirmPopup;
560 }
561}
562
567void MythControls::ResolveConflict(ActionID *conflict, int error_level,
568 const QString &key)
569{
570 if (!conflict)
571 return;
572
573 QString label;
574
575 bool error = (KeyBindings::kKeyBindingError == error_level);
576
577 if (error)
578 {
579 label = tr("This key binding conflicts with %1 in the %2 context. "
580 "Unable to bind key.")
581 .arg(conflict->GetAction(), conflict->GetContext());
582 }
583 else
584 {
585 label = tr("This key binding conflicts with %1 in the %2 context. "
586 "Do you want to bind it anyway?")
587 .arg(conflict->GetAction(), conflict->GetContext());
588 }
589
590 MythScreenStack *popupStack =
591 GetMythMainWindow()->GetStack("popup stack");
592
593 auto *confirmPopup = new MythConfirmationDialog(popupStack, label, !error);
594
595 if (!error)
596 {
597 confirmPopup->SetData(QVariant::fromValue(key));
598 confirmPopup->SetReturnEvent(this, "conflict");
599 }
600
601 if (confirmPopup->Create())
602 popupStack->AddScreen(confirmPopup);
603
604 delete conflict;
605}
606
607void MythControls::GrabKey(void) const
608{
609 /* grab a key from the user */
610 MythScreenStack *popupStack =
611 GetMythMainWindow()->GetStack("popup stack");
612
613 auto *keyGrabPopup = new KeyGrabPopupBox(popupStack);
614
615 if (keyGrabPopup->Create())
616 popupStack->AddScreen(keyGrabPopup, false);
617
618 connect(keyGrabPopup, &KeyGrabPopupBox::HaveResult,
619 this, qOverload<const QString&>(&MythControls::AddKeyToAction),
620 Qt::QueuedConnection);
621}
622
631void MythControls::AddKeyToAction(const QString& key, bool ignoreconflict)
632{
633 QString action = GetCurrentAction();
634 QString context = GetCurrentContext();
635 QStringList keys = m_bindings->GetActionKeys(context, action);
636
637 // Don't recreating an existing binding...
638 int binding_index = GetCurrentButton();
639 if ((binding_index >= (int)Action::kMaximumNumberOfBindings) ||
640 ((binding_index < keys.size()) && (keys[binding_index] == key)))
641 {
642 return;
643 }
644
645 if (!ignoreconflict)
646 {
647 // Check for first of the potential conflicts.
648 int err_level = 0;
649 ActionID *conflict = m_bindings->GetConflict(context, key, err_level);
650 if (conflict)
651 {
652 ResolveConflict(conflict, err_level, key);
653
654 return;
655 }
656 }
657
658 if (binding_index < keys.count())
659 {
660 m_bindings->ReplaceActionKey(context, action, key,
661 keys[binding_index]);
662 }
663 else
664 {
665 m_bindings->AddActionKey(context, action, key);
666 }
667
669}
670
671void MythControls::AddKeyToAction(const QString& key)
672{
673 AddKeyToAction(key, false);
674}
675
676void MythControls::customEvent(QEvent *event)
677{
678 if (event->type() == DialogCompletionEvent::kEventType)
679 {
680 auto *dce = (DialogCompletionEvent*)(event);
681
682 QString resultid = dce->GetId();
683 int buttonnum = dce->GetResult();
684
685 if (resultid == "action")
686 {
687 if (buttonnum == 0)
688 GrabKey();
689 else if (buttonnum == 1)
690 DeleteKey();
691 }
692 else if (resultid == "option")
693 {
694 if (buttonnum == 0)
695 Save();
696 else if (buttonnum == 1)
697 ChangeView();
698 else if (buttonnum == 2)
699 GetMythMainWindow()->JumpTo("Reset All Keys");
700 }
701 else if (resultid == "exit")
702 {
703 if (buttonnum == 1)
704 Save();
705 else
706 Teardown();
707
708 Close();
709 }
710 else if (resultid == "view")
711 {
712 QStringList contents;
713 QString leftcaption;
714 QString rightcaption;
715
716 if (buttonnum == 0)
717 {
718 leftcaption = tr("Contexts");
719 rightcaption = tr("Actions");
721 contents = m_bindings->GetContexts();
722 }
723 else if (buttonnum == 1)
724 {
725 leftcaption = tr("Contexts");
726 rightcaption = tr("Keys");
728 contents = m_bindings->GetContexts();
729 }
730 else if (buttonnum == 2)
731 {
732 leftcaption = tr("Keys");
733 rightcaption = tr("Contexts");
735 contents = m_bindings->GetKeys();
736 }
737 else
738 {
739 return;
740 }
741
742 m_leftDescription->SetText(leftcaption);
743 m_rightDescription->SetText(rightcaption);
744
745 SetListContents(m_leftList, contents, true);
748
749 if (GetFocusWidget() != m_leftList)
751 }
752 else if (resultid == "conflict")
753 {
754 if (buttonnum == 1)
755 {
756 QString key = dce->GetData().toString();
757 AddKeyToAction(key, true);
758 }
759 }
760
761 if (m_menuPopup)
762 m_menuPopup = nullptr;
763 }
764
765}
766
767/* vim: set expandtab tabstop=4 shiftwidth=4: */
A class that uniquely identifies an action.
Definition: action.h:85
QString GetAction(void) const
Returns the action name.
Definition: action.h:103
QString GetContext(void) const
Returns the context name.
Definition: action.h:100
static const QString kJumpContext
The statically assigned context for jump point actions.
Definition: actionset.h:80
static const QString kGlobalContext
The name of global actions.
Definition: actionset.h:82
static const unsigned int kMaximumNumberOfBindings
The maximum number of keys that can be bound to an action.
Definition: action.h:71
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:41
static const Type kEventType
Definition: mythdialogbox.h:56
Encapsulates information about the current keybindings.
Definition: keybindings.h:37
QStringList GetKeys(void) const
Returns a list of all keys bound to an action.
Definition: keybindings.cpp:49
bool AddActionKey(const QString &context_name, const QString &action_name, const QString &key)
Add a key to an action.
QString GetActionDescription(const QString &context_name, const QString &action_name) const
Get an action's description.
bool HasChanges(void) const
Definition: keybindings.h:78
QStringList GetActionKeys(const QString &context_name, const QString &action_name) const
Get an action's keys.
Definition: keybindings.cpp:95
QStringList GetContexts(void) const
Returns a list of the context names.
Definition: keybindings.cpp:58
@ kKeyBindingError
Definition: keybindings.h:47
QStringList GetKeyContexts(const QString &key) const
Get the context names in which a key is bound.
bool RemoveActionKey(const QString &context_name, const QString &action_name, const QString &key)
Unbind a key from an action.
QStringList GetActions(const QString &context) const
Get a list of the actions in a context.
Definition: keybindings.cpp:72
QStringList GetContextKeys(const QString &context) const
Get the keys within a context.
ActionID * GetConflict(const QString &context_name, const QString &key, int &level) const
Determine if adding a key would cause a conflict.
void ReplaceActionKey(const QString &context_name, const QString &action_name, const QString &newkey, const QString &oldkey)
Replace a key in an action.
Captures a key.
Definition: keygrabber.h:16
void HaveResult(QString)
Dialog asking for user confirmation.
void DeleteKey(void)
Delete the currently active key to action mapping.
void ActionButtonPressed()
Slot handling a button being pressed in the left list.
MythUIButtonList * m_leftList
Definition: mythcontrols.h:121
QList< MythUIButton * > m_actionButtons
Definition: mythcontrols.h:126
ListType m_leftListType
Definition: mythcontrols.h:133
KeyBindings * m_bindings
Definition: mythcontrols.h:129
void Teardown(void)
KeyBindings::Filter m_filters
Definition: mythcontrols.h:135
MythUIText * m_leftDescription
Definition: mythcontrols.h:124
void UpdateRightList(void)
Update the right list.
QString GetCurrentAction(void)
Get the currently selected action string.
ListType m_rightListType
Definition: mythcontrols.h:134
MythDialogBox * m_menuPopup
Definition: mythcontrols.h:127
void GrabKey(void) const
void ResolveConflict(ActionID *conflict, int error_level, const QString &key)
Resolve a potential conflict.
ViewType m_currentView
Definition: mythcontrols.h:120
static void SetListContents(MythUIButtonList *uilist, const QStringList &contents, bool arrows=false)
Set the contents of a list.
MythUIButtonList * m_rightList
Definition: mythcontrols.h:122
uint GetCurrentButton(void)
Returns the focused button, or Action::kMaximumNumberOfBindings if no buttons are focued.
void RefreshKeyInformation(void)
Updates the list of keys that are shown and the description of the action.
void LeftSelected(MythUIButtonListItem *item)
Refreshes the right list when an item in the left list is selected.
~MythControls() override
void customEvent(QEvent *event) override
QStringList m_sortedContexts
sorted list of contexts
Definition: mythcontrols.h:130
void Close(void) override
void ChangeView(void)
Change the view.
bool Create(void) override
Loads UI elements from theme.
void RightPressed(MythUIButtonListItem *item)
Slot handling a button being pressed in the left list.
void AddKeyToAction(const QString &key, bool ignoreconflict)
Add a key to the currently selected action.
void Save(void)
Definition: mythcontrols.h:96
void RightSelected(MythUIButtonListItem *item)
Refreshes key information when an item in the right list is selected.
void ChangeButtonFocus(int direction)
Change button focus in a particular direction.
QHash< QString, QStringList > m_contexts
actions for a given context
Definition: mythcontrols.h:132
MythUIText * m_rightDescription
Definition: mythcontrols.h:125
QString GetCurrentKey(void)
Get the currently selected key string.
void LeftPressed(MythUIButtonListItem *item)
Slot handling a button being pressed in the left list.
QString GetCurrentContext(void)
Get the currently selected context string.
void ShowMenu(void) override
void LoadData(const QString &hostname)
Load the settings for a particular host.
MythUIText * m_description
Definition: mythcontrols.h:123
QString GetHostName(void)
Basic menu dialog, message and a list of options.
void AddButton(const QString &title)
void SetReturnEvent(QObject *retobject, const QString &resultid)
bool Create(void) override
void JumpTo(const QString &Destination, bool Pop=true)
MythScreenStack * GetStack(const QString &Stackname)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
virtual bool NextPrevWidgetFocus(bool up_or_down)
void BuildFocusList(void)
MythUIType * GetFocusWidget(void) const
bool SetFocusWidget(MythUIType *widget=nullptr)
virtual void Close()
QString GetText(const QString &name="") const
List widget, displays list items in a variety of themeable arrangements and can trigger signals when ...
MythUIButtonListItem * GetItemCurrent() const
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
void itemClicked(MythUIButtonListItem *item)
void itemSelected(MythUIButtonListItem *item)
A single button widget.
Definition: mythuibutton.h:22
void Clicked()
All purpose text widget, displays a text string.
Definition: mythuitext.h:29
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuitext.cpp:65
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:115
The base class on which all widgets and screens are based.
Definition: mythuitype.h:86
void TakingFocus(void)
MythUIType * GetChild(const QString &name) const
Get a named child of this UIType.
Definition: mythuitype.cpp:138
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
unsigned int uint
Definition: compat.h:68
#define LOC
Main header for mythcontrols.
@ kActionsByContext
Definition: mythcontrols.h:41
@ kKeysByContext
Definition: mythcontrols.h:41
@ kContextsByKey
Definition: mythcontrols.h:41
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
def error(message)
Definition: smolt.py:409
string hostname
Definition: caa.py:17