MythTV  master
mythcontrols.cpp
Go to the documentation of this file.
1 // -*- Mode: c++ -*-
28 #include "mythcontrols.h"
29 
30 // Qt headers
31 #include <QStringList>
32 #include <QCoreApplication>
33 
34 // MythTV headers
35 #include "mythcorecontext.h"
36 #include "mythmainwindow.h"
37 
38 // MythUI headers
39 #include "mythuitext.h"
40 #include "mythuibutton.h"
41 #include "mythuibuttonlist.h"
42 #include "mythdialogbox.h"
43 
44 // MythControls headers
45 #include "keygrabber.h"
46 
47 #define LOC QString("MythControls: ")
48 #define LOC_ERR QString("MythControls, Error: ")
49 
56  : MythScreenType (parent, name)
57 {
59  m_leftList = m_rightList = nullptr;
61  m_bindings = nullptr;
62 
65 
66  m_menuPopup = nullptr;
67 }
68 
70 {
71  Teardown();
72 }
73 
75 {
76  if (m_bindings)
77  {
78  delete m_bindings;
79  m_bindings = nullptr;
80  }
81 
82  m_contexts.clear();
83 }
84 
91 {
92  // Load the theme for this screen
93  bool foundtheme = LoadWindowFromXML("controls-ui.xml", "controls", this);
94  if (!foundtheme)
95  return false;
96 
97  m_description = dynamic_cast<MythUIText *>(GetChild("description"));
98  m_leftList = dynamic_cast<MythUIButtonList *>(GetChild("leftlist"));
99  m_rightList = dynamic_cast<MythUIButtonList *>(GetChild("rightlist"));
100  m_leftDescription = dynamic_cast<MythUIText *>(GetChild("leftdesc"));
101  m_rightDescription = dynamic_cast<MythUIText *>(GetChild("rightdesc"));
102 
103  if (!m_description || !m_leftList || !m_rightList ||
105  {
106  LOG(VB_GENERAL, LOG_ERR, "Theme is missing critical theme elements.");
107  return false;
108  }
109 
110  connect(m_leftList, SIGNAL(itemSelected(MythUIButtonListItem*)),
112  connect(m_leftList, SIGNAL(itemClicked(MythUIButtonListItem*)),
114 
115  connect(m_rightList, SIGNAL(itemSelected(MythUIButtonListItem*)),
117  connect(m_rightList, SIGNAL(itemClicked(MythUIButtonListItem*)),
119  connect(m_rightList, SIGNAL(TakingFocus()),
120  SLOT(RefreshKeyInformation()));
121 
122  for (uint i = 0; i < Action::kMaximumNumberOfBindings; i++)
123  {
124  MythUIButton *button = dynamic_cast<MythUIButton *>
125  (GetChild(QString("action_%1").arg(i)));
126 
127  if (!button)
128  {
129  LOG(VB_GENERAL, LOG_ERR, LOC +
130  QString("Unable to load action button action_%1").arg(i));
131 
132  return false;
133  }
134 
135  connect(button, SIGNAL(Clicked()), SLOT(ActionButtonPressed()));
136 
137  m_actionButtons.append(button);
138  }
139 
140  BuildFocusList();
141 
143 
144  /* start off with the actions by contexts view */
147  UpdateRightList();
148 
149  return true;
150 }
151 
158 {
160  return;
161 
162  if (direction == 0)
164 }
165 
170 {
171  (void) item;
172  NextPrevWidgetFocus(true);
173 }
174 
179 {
180  (void) item;
183 }
184 
189 {
190  QString key = GetCurrentKey();
191  if (!key.isEmpty())
192  {
193  QString label = tr("Modify Action");
194 
195  MythScreenStack *popupStack =
196  GetMythMainWindow()->GetStack("popup stack");
197 
198  m_menuPopup =
199  new MythDialogBox(label, popupStack, "actionmenu");
200 
201  if (m_menuPopup->Create())
202  popupStack->AddScreen(m_menuPopup);
203 
204  m_menuPopup->SetReturnEvent(this, "action");
205 
206  m_menuPopup->AddButton(tr("Set Binding"));
207  m_menuPopup->AddButton(tr("Remove Binding"));
208  }
209  else // for blank keys, no reason to ask what to do
210  GrabKey();
211 }
212 
217 {
218  QString label = tr("Change View");
219 
220  MythScreenStack *popupStack =
221  GetMythMainWindow()->GetStack("popup stack");
222 
223  m_menuPopup =
224  new MythDialogBox(label, popupStack, "mcviewmenu");
225 
226  if (m_menuPopup->Create())
227  popupStack->AddScreen(m_menuPopup);
228 
229  m_menuPopup->SetReturnEvent(this, "view");
230 
231  m_menuPopup->AddButton(tr("Actions By Context"));
232  m_menuPopup->AddButton(tr("Contexts By Key"));
233  m_menuPopup->AddButton(tr("Keys By Context"));
234 
235 }
236 
238 {
239  QString label = tr("Options");
240 
241  MythScreenStack *popupStack =
242  GetMythMainWindow()->GetStack("popup stack");
243 
244  m_menuPopup =
245  new MythDialogBox(label, popupStack, "optionmenu");
246 
247  if (m_menuPopup->Create())
248  popupStack->AddScreen(m_menuPopup);
249 
250  m_menuPopup->SetReturnEvent(this, "option");
251 
252  m_menuPopup->AddButton(tr("Save"));
253  m_menuPopup->AddButton(tr("Change View"));
254  m_menuPopup->AddButton(tr("Reset All Keys to Defaults"));
255 }
256 
258 {
259  if (m_bindings && m_bindings->HasChanges())
260  {
261  /* prompt user to save changes */
262  QString label = tr("Save changes?");
263 
264  MythScreenStack *popupStack =
265  GetMythMainWindow()->GetStack("popup stack");
266 
267  MythConfirmationDialog *confirmPopup
268  = new MythConfirmationDialog(popupStack, label, true);
269 
270  if (confirmPopup->Create())
271  popupStack->AddScreen(confirmPopup);
272 
273  confirmPopup->SetReturnEvent(this, "exit");
274  }
275  else
277 }
278 
284 {
285  UpdateRightList();
286 }
287 
293 {
295 }
296 
297 
305  MythUIButtonList *uilist, const QStringList &contents, bool arrows)
306 {
307  // remove all strings from the current list
308  uilist->Reset();
309 
310  // add each new string
311  QStringList::const_iterator it = contents.begin();
312  for (; it != contents.end(); ++it)
313  {
314  MythUIButtonListItem *item = new MythUIButtonListItem(uilist, *it);
315  item->setDrawArrow(arrows);
316  }
317 }
318 
323 {
324  // get the selected item in the right list.
326 
327  if (!item)
328  return;
329 
330  QString rtstr = item->GetText();
331 
332  switch(m_currentView)
333  {
334  case kActionsByContext:
336  break;
337  case kKeysByContext:
339  break;
340  case kContextsByKey:
342  break;
343  }
344 }
345 
351 {
352  for (uint i = 0; i < Action::kMaximumNumberOfBindings; i++)
353  m_actionButtons.at(i)->SetText("");
354 
355  if (GetFocusWidget() == m_leftList)
356  {
357  m_description->Reset();
358  return;
359  }
360 
361  const QString context = GetCurrentContext();
362  const QString action = GetCurrentAction();
363 
364  QString desc = m_bindings->GetActionDescription(context, action);
365  m_description->SetText(tr(desc.toLatin1().constData()));
366 
367  QStringList keys = m_bindings->GetActionKeys(context, action);
368  for (int i = 0; (i < keys.count()) &&
369  (i < (int)Action::kMaximumNumberOfBindings); i++)
370  {
371  m_actionButtons.at(i)->SetText(keys[i]);
372  }
373 }
374 
375 
384 {
386  return m_leftList->GetItemCurrent()->GetText();
387 
388  if (GetFocusWidget() == m_leftList)
389  return QString();
390 
391  QString desc = m_rightList->GetItemCurrent()->GetText();
392  int loc = desc.indexOf(" => ");
393  if (loc == -1)
394  return QString(); // Should not happen
395 
397  return desc.left(loc);
398 
399  return desc.mid(loc + 4);
400 }
401 
410 {
412  {
414  {
415  return m_leftList->GetItemCurrent()->GetText();
416  }
417  return QString();
418  }
419 
420  if (GetFocusWidget() == m_leftList)
421  return QString();
422 
424  return QString();
425 
426  QString desc = m_rightList->GetItemCurrent()->GetText();
427  if (kContextList == m_leftListType &&
429  {
430  return desc;
431  }
432 
433  int loc = desc.indexOf(" => ");
434  if (loc == -1)
435  return QString(); // should not happen..
436 
438  return desc.left(loc);
439 
440  QString rv = desc.mid(loc+4);
441  if (rv == "<none>")
442  return QString();
443 
444  return rv;
445 }
446 
452 {
453  for (uint i = 0; i < Action::kMaximumNumberOfBindings; i++)
454  {
455  MythUIButton *button = m_actionButtons.at(i);
456  MythUIType *uitype = GetFocusWidget();
457  if (uitype == button)
458  return i;
459  }
460 
462 }
463 
472 {
473  MythUIButtonListItem* currentButton;
474  if (m_leftListType == kKeyList &&
475  (currentButton = m_leftList->GetItemCurrent()))
476  {
477  return currentButton->GetText();
478  }
479 
480  if (GetFocusWidget() == m_leftList)
481  return QString();
482 
484  {
485  QString context = GetCurrentContext();
486  QString action = GetCurrentAction();
487  uint b = GetCurrentButton();
488  QStringList keys = m_bindings->GetActionKeys(context, action);
489 
490  if (b < (uint)keys.count())
491  return keys[b];
492 
493  return QString();
494  }
495 
496  currentButton = m_rightList->GetItemCurrent();
497  QString desc;
498  if (currentButton)
499  desc = currentButton->GetText();
500 
501  int loc = desc.indexOf(" => ");
502  if (loc == -1)
503  return QString(); // Should not happen
504 
505 
506  if (m_rightListType == kKeyList)
507  return desc.left(loc);
508 
509  return desc.mid(loc + 4);
510 }
511 
516 void MythControls::LoadData(const QString &hostname)
517 {
518  /* create the key bindings and the tree */
521 
522  /* Alphabetic order, but jump and global at the top */
523  m_sortedContexts.sort();
526  m_sortedContexts.insert(m_sortedContexts.begin(),
528  m_sortedContexts.insert(m_sortedContexts.begin(),
530 
531  QStringList::const_iterator it = m_sortedContexts.begin();
532  for (; it != m_sortedContexts.end(); ++it)
533  {
534  QString ctx_name = *it;
535  QStringList actions = m_bindings->GetActions(ctx_name);
536  actions.sort();
537  m_contexts.insert(ctx_name, actions);
538  }
539 }
540 
548 {
549  QString context = GetCurrentContext();
550  QString key = GetCurrentKey();
551  QString action = GetCurrentAction();
552 
553  if (context.isEmpty() || key.isEmpty() || action.isEmpty())
554  {
555  LOG(VB_GENERAL, LOG_ERR,
556  "Unable to delete binding, missing information");
557  return;
558  }
559 
560  if (m_bindings->RemoveActionKey(context, action, key))
561  {
563  return;
564  }
565 
566  QString label = tr("This action is mandatory and needs at least one key "
567  "bound to it. Instead, try rebinding with another key.");
568 
569  MythScreenStack *popupStack =
570  GetMythMainWindow()->GetStack("popup stack");
571 
572  MythConfirmationDialog *confirmPopup =
573  new MythConfirmationDialog(popupStack, label, false);
574 
575  if (confirmPopup->Create())
576  {
577  confirmPopup->SetReturnEvent(this, "mandatorydelete");
578  popupStack->AddScreen(confirmPopup);
579  }
580  else
581  delete confirmPopup;
582 }
583 
588 void MythControls::ResolveConflict(ActionID *conflict, int error_level,
589  const QString &key)
590 {
591  if (!conflict)
592  return;
593 
594  QString label;
595 
596  bool error = (KeyBindings::kKeyBindingError == error_level);
597 
598  if (error)
599  label = tr("This key binding conflicts with %1 in the %2 context. "
600  "Unable to bind key.")
601  .arg(conflict->GetAction()).arg(conflict->GetContext());
602  else
603  label = tr("This key binding conflicts with %1 in the %2 context. "
604  "Do you want to bind it anyway?")
605  .arg(conflict->GetAction()).arg(conflict->GetContext());
606 
607  MythScreenStack *popupStack =
608  GetMythMainWindow()->GetStack("popup stack");
609 
610  MythConfirmationDialog *confirmPopup =
611  new MythConfirmationDialog(popupStack, label, !error);
612 
613  if (!error)
614  {
615  confirmPopup->SetData(qVariantFromValue(key));
616  confirmPopup->SetReturnEvent(this, "conflict");
617  }
618 
619  if (confirmPopup->Create())
620  popupStack->AddScreen(confirmPopup);
621 
622  delete conflict;
623 }
624 
626 {
627  /* grab a key from the user */
628  MythScreenStack *popupStack =
629  GetMythMainWindow()->GetStack("popup stack");
630 
631  KeyGrabPopupBox *keyGrabPopup = new KeyGrabPopupBox(popupStack);
632 
633  if (keyGrabPopup->Create())
634  popupStack->AddScreen(keyGrabPopup, false);
635 
636  connect(keyGrabPopup, SIGNAL(HaveResult(QString)),
637  SLOT(AddKeyToAction(QString)), Qt::QueuedConnection);
638 }
639 
648 void MythControls::AddKeyToAction(QString key, bool ignoreconflict)
649 {
650  QString action = GetCurrentAction();
651  QString context = GetCurrentContext();
652  QStringList keys = m_bindings->GetActionKeys(context, action);
653 
654  // Don't recreating an existing binding...
655  int binding_index = GetCurrentButton();
656  if ((binding_index >= (int)Action::kMaximumNumberOfBindings) ||
657  ((binding_index < keys.size()) && (keys[binding_index] == key)))
658  {
659  return;
660  }
661 
662  if (!ignoreconflict)
663  {
664  // Check for first of the potential conflicts.
665  int err_level;
666  ActionID *conflict = m_bindings->GetConflict(context, key, err_level);
667  if (conflict)
668  {
669  ResolveConflict(conflict, err_level, key);
670 
671  return;
672  }
673  }
674 
675  if (binding_index < keys.count())
676  m_bindings->ReplaceActionKey(context, action, key,
677  keys[binding_index]);
678  else
679  m_bindings->AddActionKey(context, action, key);
680 
682 }
683 
684 void MythControls::customEvent(QEvent *event)
685 {
686  if (event->type() == DialogCompletionEvent::kEventType)
687  {
689 
690  QString resultid = dce->GetId();
691  int buttonnum = dce->GetResult();
692 
693  if (resultid == "action")
694  {
695  if (buttonnum == 0)
696  GrabKey();
697  else if (buttonnum == 1)
698  DeleteKey();
699  }
700  else if (resultid == "option")
701  {
702  if (buttonnum == 0)
703  Save();
704  else if (buttonnum == 1)
705  ChangeView();
706  else if (buttonnum == 2)
707  GetMythMainWindow()->JumpTo("Reset All Keys");
708  }
709  else if (resultid == "exit")
710  {
711  if (buttonnum == 1)
712  Save();
713  else
714  Teardown();
715 
716  Close();
717  }
718  else if (resultid == "view")
719  {
720  QStringList contents;
721  QString leftcaption, rightcaption;
722 
723  if (buttonnum == 0)
724  {
725  leftcaption = tr("Contexts");
726  rightcaption = tr("Actions");
728  contents = m_bindings->GetContexts();
729  }
730  else if (buttonnum == 1)
731  {
732  leftcaption = tr("Contexts");
733  rightcaption = tr("Keys");
735  contents = m_bindings->GetContexts();
736  }
737  else if (buttonnum == 2)
738  {
739  leftcaption = tr("Keys");
740  rightcaption = tr("Contexts");
742  contents = m_bindings->GetKeys();
743  }
744  else
745  return;
746 
747  m_leftDescription->SetText(leftcaption);
748  m_rightDescription->SetText(rightcaption);
749 
750  SetListContents(m_leftList, contents, true);
752  UpdateRightList();
753 
754  if (GetFocusWidget() != m_leftList)
756  }
757  else if (resultid == "conflict")
758  {
759  if (buttonnum == 1)
760  {
761  QString key = dce->GetData().toString();
762  AddKeyToAction(key, true);
763  }
764  }
765 
766  if (m_menuPopup)
767  m_menuPopup = nullptr;
768  }
769 
770 }
771 
772 /* vim: set expandtab tabstop=4 shiftwidth=4: */
static const QString kJumpContext
The statically assigned context for jump point actions.
Definition: actionset.h:80
Main header for mythcontrols.
Dialog asking for user confirmation.
void Save(void)
Definition: mythcontrols.h:87
void GrabKey(void)
bool HasChanges(void) const
Definition: keybindings.h:71
QString GetActionDescription(const QString &context_name, const QString &action_name) const
Get an action's description.
bool Create(void) override
Definition: keygrabber.cpp:24
static void error(const char *str,...)
Definition: vbi.c:41
void RefreshKeyInformation(void)
Updates the list of keys that are shown and the description of the action.
Basic menu dialog, message and a list of options.
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:168
Encapsulates information about the current keybindings.
Definition: keybindings.h:36
MythScreenStack * GetStack(const QString &stackname)
void setDrawArrow(bool flag)
unsigned int uint
Definition: compat.h:140
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
void ReplaceActionKey(const QString &context_name, const QString &action_name, const QString &newkey, const QString &oldkey)
Replace a key in an action.
MythUIButtonList * m_rightList
Definition: mythcontrols.h:112
#define LOC
A class that uniquely identifies an action.
Definition: action.h:82
The base class on which all widgets and screens are based.
Definition: mythuitype.h:63
void BuildFocusList(void)
static Type kEventType
Definition: mythdialogbox.h:50
void customEvent(QEvent *) override
QStringList GetKeyContexts(const QString &key) const
Get the context names in which a key is bound.
KeyBindings * m_bindings
Definition: mythcontrols.h:119
ViewType m_currentView
Definition: mythcontrols.h:110
void LeftPressed(MythUIButtonListItem *)
Slot handling a button being pressed in the left list.
unsigned char b
Definition: ParseText.cpp:340
virtual void Close()
void AddButton(const QString &title, QVariant data=0, bool newMenu=false, bool setCurrent=false)
QStringList GetActions(const QString &context) const
Get a list of the actions in a context.
Definition: keybindings.cpp:71
void Close(void) override
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
MythUIButtonList * m_leftList
Definition: mythcontrols.h:111
MythUIText * m_description
Definition: mythcontrols.h:113
void TakingFocus()
void ChangeView(void)
Change the view.
Captures a key.
Definition: keygrabber.h:15
void AddKeyToAction(QString key, bool ignoreconflict=false)
Add a key to the currently selected action.
void ActionButtonPressed()
Slot handling a button being pressed in the left list.
MythUIText * m_rightDescription
Definition: mythcontrols.h:115
QList< MythUIButton * > m_actionButtons
Definition: mythcontrols.h:116
ListType m_rightListType
Definition: mythcontrols.h:124
void ShowMenu(void) override
virtual bool NextPrevWidgetFocus(bool up_or_down)
QHash< QString, QStringList > m_contexts
actions for a given context
Definition: mythcontrols.h:122
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuitext.cpp:116
void SetReturnEvent(QObject *retobject, const QString &resultid)
void RightPressed(MythUIButtonListItem *)
Slot handling a button being pressed in the left list.
string hostname
Definition: caa.py:17
QStringList GetContextKeys(const QString &context) const
Get the keys within a context.
void JumpTo(const QString &destination, bool pop=true)
const char * name
Definition: ParseText.cpp:339
List widget, displays list items in a variety of themeable arrangements and can trigger signals when ...
bool AddActionKey(const QString &context_name, const QString &action_name, const QString &key)
Add a key to an action.
uint GetCurrentButton(void)
Returns the focused button, or Action::kMaximumNumberOfBindings if no buttons are focued.
void Teardown(void)
bool Create(void) override
Loads UI elements from theme.
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
MythUIType * GetFocusWidget(void) const
A single button widget.
Definition: mythuibutton.h:21
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
MythDialogBox * m_menuPopup
Definition: mythcontrols.h:117
MythMainWindow * GetMythMainWindow(void)
QString GetCurrentContext(void)
Get the currently selected context string.
QString GetText(const QString &name="") const
QStringList GetContexts(void) const
Returns a list of the context names.
Definition: keybindings.cpp:57
MythControls(MythScreenStack *parent, const char *name)
Creates a new MythControls wizard.
void ResolveConflict(ActionID *conflict, int error_level, const QString &key)
Resolve a potential conflict.
void LeftSelected(MythUIButtonListItem *)
Refreshes the right list when an item in the left list is selected.
ActionID * GetConflict(const QString &context_name, const QString &key, int &level) const
Determine if adding a key would cause a conflict.
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
QString GetAction(void) const
Returns the action name.
Definition: action.h:108
static const QString kGlobalContext
The name of global actions.
Definition: actionset.h:82
QString GetCurrentKey(void)
Get the currently selected key string.
bool Create(void) override
void DeleteKey(void)
Delete the currently active key to action mapping.
void SetReturnEvent(QObject *retobject, const QString &resultid)
void ChangeButtonFocus(int direction)
Change button focus in a particular direction.
void RightSelected(MythUIButtonListItem *)
Refreshes key information when an item in the right list is selected.
ListType m_leftListType
Definition: mythcontrols.h:123
QString GetContext(void) const
Returns the context name.
Definition: action.h:105
void SetListContents(MythUIButtonList *uilist, const QStringList &contents, bool arrows=false)
Set the contents of a list.
bool SetFocusWidget(MythUIType *widget=nullptr)
bool RemoveActionKey(const QString &context_name, const QString &action_name, const QString &key)
Unbind a key from an action.
QString GetCurrentAction(void)
Get the currently selected action string.
void LoadData(const QString &hostname)
Load the settings for a particular host.
QStringList GetKeys(void) const
Returns a list of all keys bound to an action.
Definition: keybindings.cpp:48
Screen in which all other widgets are contained and rendered.
void UpdateRightList(void)
Update the right list.
QString GetHostName(void)
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:37
static const unsigned int kMaximumNumberOfBindings
The maximum number of keys that can be bound to an action.
Definition: action.h:69
MythUIType * GetChild(const QString &name) const
Get a named child of this UIType.
Definition: mythuitype.cpp:155
MythUIText * m_leftDescription
Definition: mythcontrols.h:114
void SetData(QVariant data)
MythUIButtonListItem * GetItemCurrent() const
bool Create(void) override
QStringList GetActionKeys(const QString &context_name, const QString &action_name) const
Get an action's keys.
Definition: keybindings.cpp:94
QStringList m_sortedContexts
sorted list of contexts
Definition: mythcontrols.h:120