MythTV  0.27pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
mythcontrols.cpp
Go to the documentation of this file.
1 // -*- Mode: c++ -*-
29 #include "mythcontrols.h"
30 
31 // Qt headers
32 #include <QStringList>
33 #include <QCoreApplication>
34 
35 // MythTV headers
36 #include "mythcorecontext.h"
37 #include "mythmainwindow.h"
38 
39 // MythUI headers
40 #include "mythuitext.h"
41 #include "mythuibutton.h"
42 #include "mythuibuttonlist.h"
43 #include "mythdialogbox.h"
44 
45 // MythControls headers
46 #include "keygrabber.h"
47 
48 #define LOC QString("MythControls: ")
49 #define LOC_ERR QString("MythControls, Error: ")
50 
57  : MythScreenType (parent, name)
58 {
60  m_leftList = m_rightList = NULL;
62  m_bindings = NULL;
63 
66 
67  m_menuPopup = NULL;
68 }
69 
71 {
72  Teardown();
73 }
74 
76 {
77  if (m_bindings)
78  {
79  delete m_bindings;
80  m_bindings = NULL;
81  }
82 
83  m_contexts.clear();
84 }
85 
92 {
93  bool foundtheme = false;
94 
95  // Load the theme for this screen
96  foundtheme = LoadWindowFromXML("controls-ui.xml", "controls", this);
97 
98  if (!foundtheme)
99  return false;
100 
101  m_description = dynamic_cast<MythUIText *>(GetChild("description"));
102  m_leftList = dynamic_cast<MythUIButtonList *>(GetChild("leftlist"));
103  m_rightList = dynamic_cast<MythUIButtonList *>(GetChild("rightlist"));
104  m_leftDescription = dynamic_cast<MythUIText *>(GetChild("leftdesc"));
105  m_rightDescription = dynamic_cast<MythUIText *>(GetChild("rightdesc"));
106 
107  if (!m_description || !m_leftList || !m_rightList ||
109  {
110  LOG(VB_GENERAL, LOG_ERR, "Theme is missing critical theme elements.");
111  return false;
112  }
113 
114  connect(m_leftList, SIGNAL(itemSelected(MythUIButtonListItem*)),
116  connect(m_leftList, SIGNAL(itemClicked(MythUIButtonListItem*)),
118 
119  connect(m_rightList, SIGNAL(itemSelected(MythUIButtonListItem*)),
121  connect(m_rightList, SIGNAL(itemClicked(MythUIButtonListItem*)),
123  connect(m_rightList, SIGNAL(TakingFocus()),
124  SLOT(RefreshKeyInformation()));
125 
126  for (uint i = 0; i < Action::kMaximumNumberOfBindings; i++)
127  {
128  MythUIButton *button = dynamic_cast<MythUIButton *>
129  (GetChild(QString("action_%1").arg(i)));
130 
131  if (!button)
132  {
133  LOG(VB_GENERAL, LOG_ERR, LOC +
134  QString("Unable to load action button action_%1").arg(i));
135 
136  return false;
137  }
138 
139  connect(button, SIGNAL(Clicked()), SLOT(ActionButtonPressed()));
140 
141  m_actionButtons.append(button);
142  }
143 
144  BuildFocusList();
145 
147 
148  /* start off with the actions by contexts view */
151  UpdateRightList();
152 
153  return true;
154 }
155 
162 {
164  return;
165 
166  if (direction == 0)
168 }
169 
174 {
175  (void) item;
176  NextPrevWidgetFocus(true);
177 }
178 
183 {
184  (void) item;
187 }
188 
193 {
194  QString key = GetCurrentKey();
195  if (!key.isEmpty())
196  {
197  QString label = tr("Modify Action");
198 
199  MythScreenStack *popupStack =
200  GetMythMainWindow()->GetStack("popup stack");
201 
202  m_menuPopup =
203  new MythDialogBox(label, popupStack, "actionmenu");
204 
205  if (m_menuPopup->Create())
206  popupStack->AddScreen(m_menuPopup);
207 
208  m_menuPopup->SetReturnEvent(this, "action");
209 
210  m_menuPopup->AddButton(tr("Set Binding"));
211  m_menuPopup->AddButton(tr("Remove Binding"));
212  }
213  else // for blank keys, no reason to ask what to do
214  GrabKey();
215 }
216 
221 {
222  QString label = tr("Change View");
223 
224  MythScreenStack *popupStack =
225  GetMythMainWindow()->GetStack("popup stack");
226 
227  m_menuPopup =
228  new MythDialogBox(label, popupStack, "mcviewmenu");
229 
230  if (m_menuPopup->Create())
231  popupStack->AddScreen(m_menuPopup);
232 
233  m_menuPopup->SetReturnEvent(this, "view");
234 
235  m_menuPopup->AddButton(tr("Actions By Context"));
236  m_menuPopup->AddButton(tr("Contexts By Key"));
237  m_menuPopup->AddButton(tr("Keys By Context"));
238 
239 }
240 
242 {
243  QString label = tr("Options");
244 
245  MythScreenStack *popupStack =
246  GetMythMainWindow()->GetStack("popup stack");
247 
248  m_menuPopup =
249  new MythDialogBox(label, popupStack, "optionmenu");
250 
251  if (m_menuPopup->Create())
252  popupStack->AddScreen(m_menuPopup);
253 
254  m_menuPopup->SetReturnEvent(this, "option");
255 
256  m_menuPopup->AddButton(tr("Save"));
257  m_menuPopup->AddButton(tr("Change View"));
258  m_menuPopup->AddButton(tr("Reset All Keys to Defaults"));
259 }
260 
262 {
263  if (m_bindings && m_bindings->HasChanges())
264  {
265  /* prompt user to save changes */
266  QString label = tr("Save changes?");
267 
268  MythScreenStack *popupStack =
269  GetMythMainWindow()->GetStack("popup stack");
270 
271  MythConfirmationDialog *confirmPopup
272  = new MythConfirmationDialog(popupStack, label, true);
273 
274  if (confirmPopup->Create())
275  popupStack->AddScreen(confirmPopup);
276 
277  confirmPopup->SetReturnEvent(this, "exit");
278  }
279  else
281 }
282 
288 {
289  UpdateRightList();
290 }
291 
297 {
299 }
300 
301 
309  MythUIButtonList *uilist, const QStringList &contents, bool arrows)
310 {
311  // remove all strings from the current list
312  uilist->Reset();
313 
314  // add each new string
315  QStringList::const_iterator it = contents.begin();
316  for (; it != contents.end(); ++it)
317  {
318  QString tmp = *it; tmp.detach();
319  MythUIButtonListItem *item = new MythUIButtonListItem(uilist, tmp);
320  item->setDrawArrow(arrows);
321  }
322 }
323 
328 {
329  // get the selected item in the right list.
331 
332  if (!item)
333  return;
334 
335  QString rtstr = item->GetText();
336 
337  switch(m_currentView)
338  {
339  case kActionsByContext:
341  break;
342  case kKeysByContext:
344  break;
345  case kContextsByKey:
347  break;
348  }
349 }
350 
356 {
357  for (uint i = 0; i < Action::kMaximumNumberOfBindings; i++)
358  m_actionButtons.at(i)->SetText("");
359 
360  if (GetFocusWidget() == m_leftList)
361  {
362  m_description->Reset();
363  return;
364  }
365 
366  const QString context = GetCurrentContext();
367  const QString action = GetCurrentAction();
368 
369  QString desc = m_bindings->GetActionDescription(context, action);
370  m_description->SetText(tr(desc.toLatin1().constData()));
371 
372  QStringList keys = m_bindings->GetActionKeys(context, action);
373  for (int i = 0; (i < keys.count()) &&
374  (i < (int)Action::kMaximumNumberOfBindings); i++)
375  {
376  m_actionButtons.at(i)->SetText(keys[i]);
377  }
378 }
379 
380 
389 {
391  return m_leftList->GetItemCurrent()->GetText();
392 
393  if (GetFocusWidget() == m_leftList)
394  return QString();
395 
396  QString desc = m_rightList->GetItemCurrent()->GetText();
397  int loc = desc.indexOf(" => ");
398  if (loc == -1)
399  return QString(); // Should not happen
400 
402  return desc.left(loc);
403 
404  return desc.mid(loc + 4);
405 }
406 
415 {
417  {
419  {
420  QString tmp = m_leftList->GetItemCurrent()->GetText();
421  tmp.detach();
422  return tmp;
423  }
424  return QString();
425  }
426 
427  if (GetFocusWidget() == m_leftList)
428  return QString();
429 
431  return QString();
432 
433  QString desc = m_rightList->GetItemCurrent()->GetText();
434  if (kContextList == m_leftListType &&
436  {
437  desc.detach();
438  return desc;
439  }
440 
441  int loc = desc.indexOf(" => ");
442  if (loc == -1)
443  return QString(); // should not happen..
444 
446  return desc.left(loc);
447 
448  QString rv = desc.mid(loc+4);
449  if (rv == "<none>")
450  return QString();
451 
452  return rv;
453 }
454 
460 {
461  for (uint i = 0; i < Action::kMaximumNumberOfBindings; i++)
462  {
463  MythUIButton *button = m_actionButtons.at(i);
464  MythUIType *uitype = GetFocusWidget();
465  if (uitype == button)
466  return i;
467  }
468 
469  return Action::kMaximumNumberOfBindings;
470 }
471 
480 {
481  MythUIButtonListItem* currentButton;
482  if (m_leftListType == kKeyList &&
483  (currentButton = m_leftList->GetItemCurrent()))
484  {
485  return currentButton->GetText();
486  }
487 
488  if (GetFocusWidget() == m_leftList)
489  return QString();
490 
492  {
493  QString context = GetCurrentContext();
494  QString action = GetCurrentAction();
495  uint b = GetCurrentButton();
496  QStringList keys = m_bindings->GetActionKeys(context, action);
497 
498  if (b < (uint)keys.count())
499  return keys[b];
500 
501  return QString();
502  }
503 
504  currentButton = m_rightList->GetItemCurrent();
505  QString desc;
506  if (currentButton)
507  desc = currentButton->GetText();
508 
509  int loc = desc.indexOf(" => ");
510  if (loc == -1)
511  return QString(); // Should not happen
512 
513 
514  if (m_rightListType == kKeyList)
515  return desc.left(loc);
516 
517  return desc.mid(loc + 4);
518 }
519 
524 void MythControls::LoadData(const QString &hostname)
525 {
526  /* create the key bindings and the tree */
527  m_bindings = new KeyBindings(hostname);
529 
530  /* Alphabetic order, but jump and global at the top */
531  m_sortedContexts.sort();
534  m_sortedContexts.insert(m_sortedContexts.begin(),
536  m_sortedContexts.insert(m_sortedContexts.begin(),
538 
539  QStringList::const_iterator it = m_sortedContexts.begin();
540  for (; it != m_sortedContexts.end(); ++it)
541  {
542  QString ctx_name = *it;
543  ctx_name.detach();
544  QStringList actions = m_bindings->GetActions(ctx_name);
545  actions.sort();
546  m_contexts.insert(ctx_name, actions);
547  }
548 }
549 
557 {
558  QString context = GetCurrentContext();
559  QString key = GetCurrentKey();
560  QString action = GetCurrentAction();
561 
562  if (context.isEmpty() || key.isEmpty() || action.isEmpty())
563  {
564  LOG(VB_GENERAL, LOG_ERR,
565  "Unable to delete binding, missing information");
566  return;
567  }
568 
569  if (m_bindings->RemoveActionKey(context, action, key))
570  {
572  return;
573  }
574 
575  QString label = tr("This action is mandatory and needs at least one key "
576  "bound to it. Instead, try rebinding with another key.");
577 
578  MythScreenStack *popupStack =
579  GetMythMainWindow()->GetStack("popup stack");
580 
581  MythConfirmationDialog *confirmPopup =
582  new MythConfirmationDialog(popupStack, label, false);
583 
584  if (confirmPopup->Create())
585  {
586  confirmPopup->SetReturnEvent(this, "mandatorydelete");
587  popupStack->AddScreen(confirmPopup);
588  }
589  else
590  delete confirmPopup;
591 }
592 
597 void MythControls::ResolveConflict(ActionID *conflict, int error_level,
598  const QString &key)
599 {
600  if (!conflict)
601  return;
602 
603  QString label;
604 
605  bool error = (KeyBindings::kKeyBindingError == error_level);
606 
607  if (error)
608  label = tr("This key binding conflicts with %1 in the %2 context. "
609  "Unable to bind key.")
610  .arg(conflict->GetAction()).arg(conflict->GetContext());
611  else
612  label = tr("This key binding conflicts with %1 in the %2 context. "
613  "Do you want to bind it anyway?")
614  .arg(conflict->GetAction()).arg(conflict->GetContext());
615 
616  MythScreenStack *popupStack =
617  GetMythMainWindow()->GetStack("popup stack");
618 
619  MythConfirmationDialog *confirmPopup =
620  new MythConfirmationDialog(popupStack, label, !error);
621 
622  if (!error)
623  {
624  confirmPopup->SetData(qVariantFromValue(key));
625  confirmPopup->SetReturnEvent(this, "conflict");
626  }
627 
628  if (confirmPopup->Create())
629  popupStack->AddScreen(confirmPopup);
630 
631  delete conflict;
632 }
633 
635 {
636  /* grab a key from the user */
637  MythScreenStack *popupStack =
638  GetMythMainWindow()->GetStack("popup stack");
639 
640  KeyGrabPopupBox *keyGrabPopup = new KeyGrabPopupBox(popupStack);
641 
642  if (keyGrabPopup->Create())
643  popupStack->AddScreen(keyGrabPopup, false);
644 
645  connect(keyGrabPopup, SIGNAL(HaveResult(QString)),
646  SLOT(AddKeyToAction(QString)), Qt::QueuedConnection);
647 }
648 
657 void MythControls::AddKeyToAction(QString key, bool ignoreconflict)
658 {
659  QString action = GetCurrentAction();
660  QString context = GetCurrentContext();
661  QStringList keys = m_bindings->GetActionKeys(context, action);
662 
663  // Don't recreating an existing binding...
664  int binding_index = GetCurrentButton();
665  if ((binding_index >= (int)Action::kMaximumNumberOfBindings) ||
666  ((binding_index < keys.size()) && (keys[binding_index] == key)))
667  {
668  return;
669  }
670 
671  if (!ignoreconflict)
672  {
673  // Check for first of the potential conflicts.
674  int err_level;
675  ActionID *conflict = m_bindings->GetConflict(context, key, err_level);
676  if (conflict)
677  {
678  ResolveConflict(conflict, err_level, key);
679 
680  return;
681  }
682  }
683 
684  if (binding_index < keys.count())
685  m_bindings->ReplaceActionKey(context, action, key,
686  keys[binding_index]);
687  else
688  m_bindings->AddActionKey(context, action, key);
689 
691 }
692 
693 void MythControls::customEvent(QEvent *event)
694 {
695  if (event->type() == DialogCompletionEvent::kEventType)
696  {
698 
699  QString resultid = dce->GetId();
700  int buttonnum = dce->GetResult();
701 
702  if (resultid == "action")
703  {
704  if (buttonnum == 0)
705  GrabKey();
706  else if (buttonnum == 1)
707  DeleteKey();
708  }
709  else if (resultid == "option")
710  {
711  if (buttonnum == 0)
712  Save();
713  else if (buttonnum == 1)
714  ChangeView();
715  else if (buttonnum == 2)
716  GetMythMainWindow()->JumpTo("Reset All Keys");
717  }
718  else if (resultid == "exit")
719  {
720  if (buttonnum == 1)
721  Save();
722  else
723  Teardown();
724 
725  Close();
726  }
727  else if (resultid == "view")
728  {
729  QStringList contents;
730  QString leftcaption, rightcaption;
731 
732  if (buttonnum == 0)
733  {
734  leftcaption = tr("Contexts");
735  rightcaption = tr("Actions");
737  contents = m_bindings->GetContexts();
738  }
739  else if (buttonnum == 1)
740  {
741  leftcaption = tr("Contexts");
742  rightcaption = tr("Keys");
744  contents = m_bindings->GetContexts();
745  }
746  else if (buttonnum == 2)
747  {
748  leftcaption = tr("Keys");
749  rightcaption = tr("Contexts");
751  contents = m_bindings->GetKeys();
752  }
753  else
754  return;
755 
756  m_leftDescription->SetText(leftcaption);
757  m_rightDescription->SetText(rightcaption);
758 
759  SetListContents(m_leftList, contents, true);
761  UpdateRightList();
762 
763  if (GetFocusWidget() != m_leftList)
765  }
766  else if (resultid == "conflict")
767  {
768  if (buttonnum == 1)
769  {
770  QString key = dce->GetData().toString();
771  AddKeyToAction(key, true);
772  }
773  }
774 
775  if (m_menuPopup)
776  m_menuPopup = NULL;
777  }
778 
779 }
780 
781 /* vim: set expandtab tabstop=4 shiftwidth=4: */