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 
51 {
52  Teardown();
53 }
54 
56 {
57  if (m_bindings)
58  {
59  delete m_bindings;
60  m_bindings = nullptr;
61  }
62 
63  m_contexts.clear();
64 }
65 
72 {
73  // Load the theme for this screen
74  bool foundtheme = LoadWindowFromXML("controls-ui.xml", "controls", this);
75  if (!foundtheme)
76  return false;
77 
78  m_description = dynamic_cast<MythUIText *>(GetChild("description"));
79  m_leftList = dynamic_cast<MythUIButtonList *>(GetChild("leftlist"));
80  m_rightList = dynamic_cast<MythUIButtonList *>(GetChild("rightlist"));
81  m_leftDescription = dynamic_cast<MythUIText *>(GetChild("leftdesc"));
82  m_rightDescription = dynamic_cast<MythUIText *>(GetChild("rightdesc"));
83 
84  if (!m_description || !m_leftList || !m_rightList ||
86  {
87  LOG(VB_GENERAL, LOG_ERR, "Theme is missing critical theme elements.");
88  return false;
89  }
90 
91  connect(m_leftList, SIGNAL(itemSelected(MythUIButtonListItem*)),
93  connect(m_leftList, SIGNAL(itemClicked(MythUIButtonListItem*)),
95 
96  connect(m_rightList, SIGNAL(itemSelected(MythUIButtonListItem*)),
98  connect(m_rightList, SIGNAL(itemClicked(MythUIButtonListItem*)),
100  connect(m_rightList, SIGNAL(TakingFocus()),
101  SLOT(RefreshKeyInformation()));
102 
103  for (uint i = 0; i < Action::kMaximumNumberOfBindings; i++)
104  {
105  MythUIButton *button = dynamic_cast<MythUIButton *>
106  (GetChild(QString("action_%1").arg(i)));
107 
108  if (!button)
109  {
110  LOG(VB_GENERAL, LOG_ERR, LOC +
111  QString("Unable to load action button action_%1").arg(i));
112 
113  return false;
114  }
115 
116  connect(button, SIGNAL(Clicked()), SLOT(ActionButtonPressed()));
117 
118  m_actionButtons.append(button);
119  }
120 
121  BuildFocusList();
122 
124 
125  /* start off with the actions by contexts view */
128  UpdateRightList();
129 
130  return true;
131 }
132 
139 {
141  return;
142 
143  if (direction == 0)
145 }
146 
151 {
152  (void) item;
153  NextPrevWidgetFocus(true);
154 }
155 
160 {
161  (void) item;
164 }
165 
170 {
171  QString key = GetCurrentKey();
172  if (!key.isEmpty())
173  {
174  QString label = tr("Modify Action");
175 
176  MythScreenStack *popupStack =
177  GetMythMainWindow()->GetStack("popup stack");
178 
179  m_menuPopup =
180  new MythDialogBox(label, popupStack, "actionmenu");
181 
182  if (m_menuPopup->Create())
183  popupStack->AddScreen(m_menuPopup);
184 
185  m_menuPopup->SetReturnEvent(this, "action");
186 
187  m_menuPopup->AddButton(tr("Set Binding"));
188  m_menuPopup->AddButton(tr("Remove Binding"));
189  }
190  else // for blank keys, no reason to ask what to do
191  GrabKey();
192 }
193 
198 {
199  QString label = tr("Change View");
200 
201  MythScreenStack *popupStack =
202  GetMythMainWindow()->GetStack("popup stack");
203 
204  m_menuPopup =
205  new MythDialogBox(label, popupStack, "mcviewmenu");
206 
207  if (m_menuPopup->Create())
208  popupStack->AddScreen(m_menuPopup);
209 
210  m_menuPopup->SetReturnEvent(this, "view");
211 
212  m_menuPopup->AddButton(tr("Actions By Context"));
213  m_menuPopup->AddButton(tr("Contexts By Key"));
214  m_menuPopup->AddButton(tr("Keys By Context"));
215 
216 }
217 
219 {
220  QString label = tr("Options");
221 
222  MythScreenStack *popupStack =
223  GetMythMainWindow()->GetStack("popup stack");
224 
225  m_menuPopup =
226  new MythDialogBox(label, popupStack, "optionmenu");
227 
228  if (m_menuPopup->Create())
229  popupStack->AddScreen(m_menuPopup);
230 
231  m_menuPopup->SetReturnEvent(this, "option");
232 
233  m_menuPopup->AddButton(tr("Save"));
234  m_menuPopup->AddButton(tr("Change View"));
235  m_menuPopup->AddButton(tr("Reset All Keys to Defaults"));
236 }
237 
239 {
240  if (m_bindings && m_bindings->HasChanges())
241  {
242  /* prompt user to save changes */
243  QString label = tr("Save changes?");
244 
245  MythScreenStack *popupStack =
246  GetMythMainWindow()->GetStack("popup stack");
247 
248  MythConfirmationDialog *confirmPopup
249  = new MythConfirmationDialog(popupStack, label, true);
250 
251  if (confirmPopup->Create())
252  popupStack->AddScreen(confirmPopup);
253 
254  confirmPopup->SetReturnEvent(this, "exit");
255  }
256  else
258 }
259 
265 {
266  UpdateRightList();
267 }
268 
274 {
276 }
277 
278 
286  MythUIButtonList *uilist, const QStringList &contents, bool arrows)
287 {
288  // remove all strings from the current list
289  uilist->Reset();
290 
291  // add each new string
292  QStringList::const_iterator it = contents.begin();
293  for (; it != contents.end(); ++it)
294  {
295  MythUIButtonListItem *item = new MythUIButtonListItem(uilist, *it);
296  item->setDrawArrow(arrows);
297  }
298 }
299 
304 {
305  // get the selected item in the right list.
307 
308  if (!item)
309  return;
310 
311  QString rtstr = item->GetText();
312 
313  switch(m_currentView)
314  {
315  case kActionsByContext:
317  break;
318  case kKeysByContext:
320  break;
321  case kContextsByKey:
323  break;
324  }
325 }
326 
332 {
333  for (uint i = 0; i < Action::kMaximumNumberOfBindings; i++)
334  m_actionButtons.at(i)->SetText("");
335 
336  if (GetFocusWidget() == m_leftList)
337  {
338  m_description->Reset();
339  return;
340  }
341 
342  const QString context = GetCurrentContext();
343  const QString action = GetCurrentAction();
344 
345  QString desc = m_bindings->GetActionDescription(context, action);
346  m_description->SetText(tr(desc.toLatin1().constData()));
347 
348  QStringList keys = m_bindings->GetActionKeys(context, action);
349  for (int i = 0; (i < keys.count()) &&
350  (i < (int)Action::kMaximumNumberOfBindings); i++)
351  {
352  m_actionButtons.at(i)->SetText(keys[i]);
353  }
354 }
355 
356 
365 {
367  return m_leftList->GetItemCurrent()->GetText();
368 
369  if (GetFocusWidget() == m_leftList)
370  return QString();
371 
372  QString desc = m_rightList->GetItemCurrent()->GetText();
373  int loc = desc.indexOf(" => ");
374  if (loc == -1)
375  return QString(); // Should not happen
376 
378  return desc.left(loc);
379 
380  return desc.mid(loc + 4);
381 }
382 
391 {
393  {
395  {
396  return m_leftList->GetItemCurrent()->GetText();
397  }
398  return QString();
399  }
400 
401  if (GetFocusWidget() == m_leftList)
402  return QString();
403 
405  return QString();
406 
407  QString desc = m_rightList->GetItemCurrent()->GetText();
408  if (kContextList == m_leftListType &&
410  {
411  return desc;
412  }
413 
414  int loc = desc.indexOf(" => ");
415  if (loc == -1)
416  return QString(); // should not happen..
417 
419  return desc.left(loc);
420 
421  QString rv = desc.mid(loc+4);
422  if (rv == "<none>")
423  return QString();
424 
425  return rv;
426 }
427 
433 {
434  for (uint i = 0; i < Action::kMaximumNumberOfBindings; i++)
435  {
436  MythUIButton *button = m_actionButtons.at(i);
437  MythUIType *uitype = GetFocusWidget();
438  if (uitype == button)
439  return i;
440  }
441 
443 }
444 
453 {
454  MythUIButtonListItem* currentButton;
455  if (m_leftListType == kKeyList &&
456  (currentButton = m_leftList->GetItemCurrent()))
457  {
458  return currentButton->GetText();
459  }
460 
461  if (GetFocusWidget() == m_leftList)
462  return QString();
463 
465  {
466  QString context = GetCurrentContext();
467  QString action = GetCurrentAction();
468  uint b = GetCurrentButton();
469  QStringList keys = m_bindings->GetActionKeys(context, action);
470 
471  if (b < (uint)keys.count())
472  return keys[b];
473 
474  return QString();
475  }
476 
477  currentButton = m_rightList->GetItemCurrent();
478  QString desc;
479  if (currentButton)
480  desc = currentButton->GetText();
481 
482  int loc = desc.indexOf(" => ");
483  if (loc == -1)
484  return QString(); // Should not happen
485 
486 
487  if (m_rightListType == kKeyList)
488  return desc.left(loc);
489 
490  return desc.mid(loc + 4);
491 }
492 
497 void MythControls::LoadData(const QString &hostname)
498 {
499  /* create the key bindings and the tree */
502 
503  /* Alphabetic order, but jump and global at the top */
504  m_sortedContexts.sort();
507  m_sortedContexts.insert(m_sortedContexts.begin(),
509  m_sortedContexts.insert(m_sortedContexts.begin(),
511 
512  QStringList::const_iterator it = m_sortedContexts.begin();
513  for (; it != m_sortedContexts.end(); ++it)
514  {
515  QString ctx_name = *it;
516  QStringList actions = m_bindings->GetActions(ctx_name);
517  actions.sort();
518  m_contexts.insert(ctx_name, actions);
519  }
520 }
521 
529 {
530  QString context = GetCurrentContext();
531  QString key = GetCurrentKey();
532  QString action = GetCurrentAction();
533 
534  if (context.isEmpty() || key.isEmpty() || action.isEmpty())
535  {
536  LOG(VB_GENERAL, LOG_ERR,
537  "Unable to delete binding, missing information");
538  return;
539  }
540 
541  if (m_bindings->RemoveActionKey(context, action, key))
542  {
544  return;
545  }
546 
547  QString label = tr("This action is mandatory and needs at least one key "
548  "bound to it. Instead, try rebinding with another key.");
549 
550  MythScreenStack *popupStack =
551  GetMythMainWindow()->GetStack("popup stack");
552 
553  MythConfirmationDialog *confirmPopup =
554  new MythConfirmationDialog(popupStack, label, false);
555 
556  if (confirmPopup->Create())
557  {
558  confirmPopup->SetReturnEvent(this, "mandatorydelete");
559  popupStack->AddScreen(confirmPopup);
560  }
561  else
562  delete confirmPopup;
563 }
564 
569 void MythControls::ResolveConflict(ActionID *conflict, int error_level,
570  const QString &key)
571 {
572  if (!conflict)
573  return;
574 
575  QString label;
576 
577  bool error = (KeyBindings::kKeyBindingError == error_level);
578 
579  if (error)
580  label = tr("This key binding conflicts with %1 in the %2 context. "
581  "Unable to bind key.")
582  .arg(conflict->GetAction()).arg(conflict->GetContext());
583  else
584  label = tr("This key binding conflicts with %1 in the %2 context. "
585  "Do you want to bind it anyway?")
586  .arg(conflict->GetAction()).arg(conflict->GetContext());
587 
588  MythScreenStack *popupStack =
589  GetMythMainWindow()->GetStack("popup stack");
590 
591  MythConfirmationDialog *confirmPopup =
592  new MythConfirmationDialog(popupStack, label, !error);
593 
594  if (!error)
595  {
596  confirmPopup->SetData(qVariantFromValue(key));
597  confirmPopup->SetReturnEvent(this, "conflict");
598  }
599 
600  if (confirmPopup->Create())
601  popupStack->AddScreen(confirmPopup);
602 
603  delete conflict;
604 }
605 
607 {
608  /* grab a key from the user */
609  MythScreenStack *popupStack =
610  GetMythMainWindow()->GetStack("popup stack");
611 
612  KeyGrabPopupBox *keyGrabPopup = new KeyGrabPopupBox(popupStack);
613 
614  if (keyGrabPopup->Create())
615  popupStack->AddScreen(keyGrabPopup, false);
616 
617  connect(keyGrabPopup, SIGNAL(HaveResult(QString)),
618  SLOT(AddKeyToAction(QString)), Qt::QueuedConnection);
619 }
620 
629 void MythControls::AddKeyToAction(const QString& key, bool ignoreconflict)
630 {
631  QString action = GetCurrentAction();
632  QString context = GetCurrentContext();
633  QStringList keys = m_bindings->GetActionKeys(context, action);
634 
635  // Don't recreating an existing binding...
636  int binding_index = GetCurrentButton();
637  if ((binding_index >= (int)Action::kMaximumNumberOfBindings) ||
638  ((binding_index < keys.size()) && (keys[binding_index] == key)))
639  {
640  return;
641  }
642 
643  if (!ignoreconflict)
644  {
645  // Check for first of the potential conflicts.
646  int err_level;
647  ActionID *conflict = m_bindings->GetConflict(context, key, err_level);
648  if (conflict)
649  {
650  ResolveConflict(conflict, err_level, key);
651 
652  return;
653  }
654  }
655 
656  if (binding_index < keys.count())
657  m_bindings->ReplaceActionKey(context, action, key,
658  keys[binding_index]);
659  else
660  m_bindings->AddActionKey(context, action, key);
661 
663 }
664 
665 void MythControls::customEvent(QEvent *event)
666 {
667  if (event->type() == DialogCompletionEvent::kEventType)
668  {
670 
671  QString resultid = dce->GetId();
672  int buttonnum = dce->GetResult();
673 
674  if (resultid == "action")
675  {
676  if (buttonnum == 0)
677  GrabKey();
678  else if (buttonnum == 1)
679  DeleteKey();
680  }
681  else if (resultid == "option")
682  {
683  if (buttonnum == 0)
684  Save();
685  else if (buttonnum == 1)
686  ChangeView();
687  else if (buttonnum == 2)
688  GetMythMainWindow()->JumpTo("Reset All Keys");
689  }
690  else if (resultid == "exit")
691  {
692  if (buttonnum == 1)
693  Save();
694  else
695  Teardown();
696 
697  Close();
698  }
699  else if (resultid == "view")
700  {
701  QStringList contents;
702  QString leftcaption, rightcaption;
703 
704  if (buttonnum == 0)
705  {
706  leftcaption = tr("Contexts");
707  rightcaption = tr("Actions");
709  contents = m_bindings->GetContexts();
710  }
711  else if (buttonnum == 1)
712  {
713  leftcaption = tr("Contexts");
714  rightcaption = tr("Keys");
716  contents = m_bindings->GetContexts();
717  }
718  else if (buttonnum == 2)
719  {
720  leftcaption = tr("Keys");
721  rightcaption = tr("Contexts");
723  contents = m_bindings->GetKeys();
724  }
725  else
726  return;
727 
728  m_leftDescription->SetText(leftcaption);
729  m_rightDescription->SetText(rightcaption);
730 
731  SetListContents(m_leftList, contents, true);
733  UpdateRightList();
734 
735  if (GetFocusWidget() != m_leftList)
737  }
738  else if (resultid == "conflict")
739  {
740  if (buttonnum == 1)
741  {
742  QString key = dce->GetData().toString();
743  AddKeyToAction(key, true);
744  }
745  }
746 
747  if (m_menuPopup)
748  m_menuPopup = nullptr;
749  }
750 
751 }
752 
753 /* 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:93
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:16
static void error(const char *str,...)
Definition: vbi.c:42
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:136
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:118
#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:125
ViewType m_currentView
Definition: mythcontrols.h:116
void LeftPressed(MythUIButtonListItem *)
Slot handling a button being pressed in the left list.
unsigned char b
Definition: ParseText.cpp:329
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
void AddKeyToAction(const QString &key, bool ignoreconflict=false)
Add a key to the currently selected action.
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
MythUIButtonList * m_leftList
Definition: mythcontrols.h:117
MythUIText * m_description
Definition: mythcontrols.h:119
void TakingFocus()
void ChangeView(void)
Change the view.
Captures a key.
Definition: keygrabber.h:15
void ActionButtonPressed()
Slot handling a button being pressed in the left list.
MythUIText * m_rightDescription
Definition: mythcontrols.h:121
QList< MythUIButton * > m_actionButtons
Definition: mythcontrols.h:122
ListType m_rightListType
Definition: mythcontrols.h:130
void ShowMenu(void) override
virtual bool NextPrevWidgetFocus(bool up_or_down)
QHash< QString, QStringList > m_contexts
actions for a given context
Definition: mythcontrols.h:128
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuitext.cpp:84
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)
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:123
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
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:129
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
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:132
MythUIText * m_leftDescription
Definition: mythcontrols.h:120
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:126