00001
00029 #include "mythcontrols.h"
00030
00031
00032 #include <QStringList>
00033 #include <QCoreApplication>
00034
00035
00036 #include "mythcorecontext.h"
00037 #include "mythmainwindow.h"
00038
00039
00040 #include "mythuitext.h"
00041 #include "mythuibutton.h"
00042 #include "mythuibuttonlist.h"
00043 #include "mythdialogbox.h"
00044
00045
00046 #include "keygrabber.h"
00047
00048 #define LOC QString("MythControls: ")
00049 #define LOC_ERR QString("MythControls, Error: ")
00050
00056 MythControls::MythControls(MythScreenStack *parent, const char *name)
00057 : MythScreenType (parent, name)
00058 {
00059 m_currentView = kActionsByContext;
00060 m_leftList = m_rightList = NULL;
00061 m_description = m_leftDescription = m_rightDescription = NULL;
00062 m_bindings = NULL;
00063
00064 m_leftListType = kContextList;
00065 m_rightListType = kActionList;
00066
00067 m_menuPopup = NULL;
00068 }
00069
00070 MythControls::~MythControls()
00071 {
00072 Teardown();
00073 }
00074
00075 void MythControls::Teardown(void)
00076 {
00077 if (m_bindings)
00078 {
00079 delete m_bindings;
00080 m_bindings = NULL;
00081 }
00082
00083 m_contexts.clear();
00084 }
00085
00091 bool MythControls::Create(void)
00092 {
00093 bool foundtheme = false;
00094
00095
00096 foundtheme = LoadWindowFromXML("controls-ui.xml", "controls", this);
00097
00098 if (!foundtheme)
00099 return false;
00100
00101 m_description = dynamic_cast<MythUIText *>(GetChild("description"));
00102 m_leftList = dynamic_cast<MythUIButtonList *>(GetChild("leftlist"));
00103 m_rightList = dynamic_cast<MythUIButtonList *>(GetChild("rightlist"));
00104 m_leftDescription = dynamic_cast<MythUIText *>(GetChild("leftdesc"));
00105 m_rightDescription = dynamic_cast<MythUIText *>(GetChild("rightdesc"));
00106
00107 if (!m_description || !m_leftList || !m_rightList ||
00108 !m_leftDescription || !m_rightDescription)
00109 {
00110 LOG(VB_GENERAL, LOG_ERR, "Theme is missing critical theme elements.");
00111 return false;
00112 }
00113
00114 connect(m_leftList, SIGNAL(itemSelected(MythUIButtonListItem*)),
00115 SLOT(LeftSelected(MythUIButtonListItem*)));
00116 connect(m_leftList, SIGNAL(itemClicked(MythUIButtonListItem*)),
00117 SLOT(LeftPressed(MythUIButtonListItem*)));
00118
00119 connect(m_rightList, SIGNAL(itemSelected(MythUIButtonListItem*)),
00120 SLOT(RightSelected(MythUIButtonListItem*)));
00121 connect(m_rightList, SIGNAL(itemClicked(MythUIButtonListItem*)),
00122 SLOT(RightPressed(MythUIButtonListItem*)));
00123 connect(m_rightList, SIGNAL(TakingFocus()),
00124 SLOT(RefreshKeyInformation()));
00125
00126 for (uint i = 0; i < Action::kMaximumNumberOfBindings; i++)
00127 {
00128 MythUIButton *button = dynamic_cast<MythUIButton *>
00129 (GetChild(QString("action_%1").arg(i)));
00130
00131 if (!button)
00132 {
00133 LOG(VB_GENERAL, LOG_ERR, LOC +
00134 QString("Unable to load action button action_%1").arg(i));
00135
00136 return false;
00137 }
00138
00139 connect(button, SIGNAL(Clicked()), SLOT(ActionButtonPressed()));
00140
00141 m_actionButtons.append(button);
00142 }
00143
00144 BuildFocusList();
00145
00146 LoadData(gCoreContext->GetHostName());
00147
00148
00149 m_currentView = kActionsByContext;
00150 SetListContents(m_leftList, m_bindings->GetContexts(), true);
00151 UpdateRightList();
00152
00153 return true;
00154 }
00155
00161 void MythControls::ChangeButtonFocus(int direction)
00162 {
00163 if ((m_leftListType != kContextList) || (m_rightListType != kActionList))
00164 return;
00165
00166 if (direction == 0)
00167 SetFocusWidget(m_actionButtons.at(0));
00168 }
00169
00173 void MythControls::LeftPressed(MythUIButtonListItem *item)
00174 {
00175 (void) item;
00176 NextPrevWidgetFocus(true);
00177 }
00178
00182 void MythControls::RightPressed(MythUIButtonListItem *item)
00183 {
00184 (void) item;
00185 if (m_currentView == kActionsByContext)
00186 ChangeButtonFocus(0);
00187 }
00188
00192 void MythControls::ActionButtonPressed()
00193 {
00194 QString key = GetCurrentKey();
00195 if (!key.isEmpty())
00196 {
00197 QString label = tr("Modify Action");
00198
00199 MythScreenStack *popupStack =
00200 GetMythMainWindow()->GetStack("popup stack");
00201
00202 m_menuPopup =
00203 new MythDialogBox(label, popupStack, "actionmenu");
00204
00205 if (m_menuPopup->Create())
00206 popupStack->AddScreen(m_menuPopup);
00207
00208 m_menuPopup->SetReturnEvent(this, "action");
00209
00210 m_menuPopup->AddButton(tr("Set Binding"));
00211 m_menuPopup->AddButton(tr("Remove Binding"));
00212 }
00213 else
00214 GrabKey();
00215 }
00216
00220 void MythControls::ChangeView(void)
00221 {
00222 QString label = tr("Change View");
00223
00224 MythScreenStack *popupStack =
00225 GetMythMainWindow()->GetStack("popup stack");
00226
00227 m_menuPopup =
00228 new MythDialogBox(label, popupStack, "mcviewmenu");
00229
00230 if (m_menuPopup->Create())
00231 popupStack->AddScreen(m_menuPopup);
00232
00233 m_menuPopup->SetReturnEvent(this, "view");
00234
00235 m_menuPopup->AddButton(tr("Actions By Context"));
00236 m_menuPopup->AddButton(tr("Contexts By Key"));
00237 m_menuPopup->AddButton(tr("Keys By Context"));
00238
00239 }
00240
00241 void MythControls::ShowMenu()
00242 {
00243 QString label = tr("Options");
00244
00245 MythScreenStack *popupStack =
00246 GetMythMainWindow()->GetStack("popup stack");
00247
00248 m_menuPopup =
00249 new MythDialogBox(label, popupStack, "optionmenu");
00250
00251 if (m_menuPopup->Create())
00252 popupStack->AddScreen(m_menuPopup);
00253
00254 m_menuPopup->SetReturnEvent(this, "option");
00255
00256 m_menuPopup->AddButton(tr("Save"));
00257 m_menuPopup->AddButton(tr("Change View"));
00258 m_menuPopup->AddButton(tr("Reset All Keys to Defaults"));
00259 }
00260
00261 void MythControls::Close()
00262 {
00263 if (m_bindings && m_bindings->HasChanges())
00264 {
00265
00266 QString label = tr("Save changes?");
00267
00268 MythScreenStack *popupStack =
00269 GetMythMainWindow()->GetStack("popup stack");
00270
00271 MythConfirmationDialog *confirmPopup
00272 = new MythConfirmationDialog(popupStack, label, true);
00273
00274 if (confirmPopup->Create())
00275 popupStack->AddScreen(confirmPopup);
00276
00277 confirmPopup->SetReturnEvent(this, "exit");
00278 }
00279 else
00280 MythScreenType::Close();
00281 }
00282
00287 void MythControls::LeftSelected(MythUIButtonListItem*)
00288 {
00289 UpdateRightList();
00290 }
00291
00296 void MythControls::RightSelected(MythUIButtonListItem*)
00297 {
00298 RefreshKeyInformation();
00299 }
00300
00301
00308 void MythControls::SetListContents(
00309 MythUIButtonList *uilist, const QStringList &contents, bool arrows)
00310 {
00311
00312 uilist->Reset();
00313
00314
00315 QStringList::const_iterator it = contents.begin();
00316 for (; it != contents.end(); ++it)
00317 {
00318 QString tmp = *it; tmp.detach();
00319 MythUIButtonListItem *item = new MythUIButtonListItem(uilist, tmp);
00320 item->setDrawArrow(arrows);
00321 }
00322 }
00323
00327 void MythControls::UpdateRightList(void)
00328 {
00329
00330 MythUIButtonListItem *item = m_leftList->GetItemCurrent();
00331
00332 if (!item)
00333 return;
00334
00335 QString rtstr = item->GetText();
00336
00337 switch(m_currentView)
00338 {
00339 case kActionsByContext:
00340 SetListContents(m_rightList, m_contexts[rtstr]);
00341 break;
00342 case kKeysByContext:
00343 SetListContents(m_rightList, m_bindings->GetContextKeys(rtstr));
00344 break;
00345 case kContextsByKey:
00346 SetListContents(m_rightList, m_bindings->GetKeyContexts(rtstr));
00347 break;
00348 }
00349 }
00350
00355 void MythControls::RefreshKeyInformation(void)
00356 {
00357 for (uint i = 0; i < Action::kMaximumNumberOfBindings; i++)
00358 m_actionButtons.at(i)->SetText("");
00359
00360 if (GetFocusWidget() == m_leftList)
00361 {
00362 m_description->Reset();
00363 return;
00364 }
00365
00366 const QString context = GetCurrentContext();
00367 const QString action = GetCurrentAction();
00368
00369 QString desc = m_bindings->GetActionDescription(context, action);
00370 m_description->SetText(tr(desc.toAscii().constData()));
00371
00372 QStringList keys = m_bindings->GetActionKeys(context, action);
00373 for (int i = 0; (i < keys.count()) &&
00374 (i < (int)Action::kMaximumNumberOfBindings); i++)
00375 {
00376 m_actionButtons.at(i)->SetText(keys[i]);
00377 }
00378 }
00379
00380
00388 QString MythControls::GetCurrentContext(void)
00389 {
00390 if (m_leftListType == kContextList)
00391 return m_leftList->GetItemCurrent()->GetText();
00392
00393 if (GetFocusWidget() == m_leftList)
00394 return QString();
00395
00396 QString desc = m_rightList->GetItemCurrent()->GetText();
00397 int loc = desc.indexOf(" => ");
00398 if (loc == -1)
00399 return QString();
00400
00401 if (m_rightListType == kContextList)
00402 return desc.left(loc);
00403
00404 return desc.mid(loc + 4);
00405 }
00406
00414 QString MythControls::GetCurrentAction(void)
00415 {
00416 if (m_leftListType == kActionList)
00417 {
00418 if (m_leftList && m_leftList->GetItemCurrent())
00419 {
00420 QString tmp = m_leftList->GetItemCurrent()->GetText();
00421 tmp.detach();
00422 return tmp;
00423 }
00424 return QString();
00425 }
00426
00427 if (GetFocusWidget() == m_leftList)
00428 return QString();
00429
00430 if (!m_rightList || !m_rightList->GetItemCurrent())
00431 return QString();
00432
00433 QString desc = m_rightList->GetItemCurrent()->GetText();
00434 if (kContextList == m_leftListType &&
00435 kActionList == m_rightListType)
00436 {
00437 desc.detach();
00438 return desc;
00439 }
00440
00441 int loc = desc.indexOf(" => ");
00442 if (loc == -1)
00443 return QString();
00444
00445 if (m_rightListType == kActionList)
00446 return desc.left(loc);
00447
00448 QString rv = desc.mid(loc+4);
00449 if (rv == "<none>")
00450 return QString();
00451
00452 return rv;
00453 }
00454
00459 uint MythControls::GetCurrentButton(void)
00460 {
00461 for (uint i = 0; i < Action::kMaximumNumberOfBindings; i++)
00462 {
00463 MythUIButton *button = m_actionButtons.at(i);
00464 MythUIType *uitype = GetFocusWidget();
00465 if (uitype == button)
00466 return i;
00467 }
00468
00469 return Action::kMaximumNumberOfBindings;
00470 }
00471
00479 QString MythControls::GetCurrentKey(void)
00480 {
00481 MythUIButtonListItem* currentButton;
00482 if (m_leftListType == kKeyList &&
00483 (currentButton = m_leftList->GetItemCurrent()))
00484 {
00485 return currentButton->GetText();
00486 }
00487
00488 if (GetFocusWidget() == m_leftList)
00489 return QString();
00490
00491 if ((m_leftListType == kContextList) && (m_rightListType == kActionList))
00492 {
00493 QString context = GetCurrentContext();
00494 QString action = GetCurrentAction();
00495 uint b = GetCurrentButton();
00496 QStringList keys = m_bindings->GetActionKeys(context, action);
00497
00498 if (b < (uint)keys.count())
00499 return keys[b];
00500
00501 return QString();
00502 }
00503
00504 currentButton = m_rightList->GetItemCurrent();
00505 QString desc;
00506 if (currentButton)
00507 desc = currentButton->GetText();
00508
00509 int loc = desc.indexOf(" => ");
00510 if (loc == -1)
00511 return QString();
00512
00513
00514 if (m_rightListType == kKeyList)
00515 return desc.left(loc);
00516
00517 return desc.mid(loc + 4);
00518 }
00519
00524 void MythControls::LoadData(const QString &hostname)
00525 {
00526
00527 m_bindings = new KeyBindings(hostname);
00528 m_sortedContexts = m_bindings->GetContexts();
00529
00530
00531 m_sortedContexts.sort();
00532 m_sortedContexts.removeAll(ActionSet::kJumpContext);
00533 m_sortedContexts.removeAll(ActionSet::kGlobalContext);
00534 m_sortedContexts.insert(m_sortedContexts.begin(),
00535 ActionSet::kGlobalContext);
00536 m_sortedContexts.insert(m_sortedContexts.begin(),
00537 ActionSet::kJumpContext);
00538
00539 QStringList::const_iterator it = m_sortedContexts.begin();
00540 for (; it != m_sortedContexts.end(); ++it)
00541 {
00542 QString ctx_name = *it;
00543 ctx_name.detach();
00544 QStringList actions = m_bindings->GetActions(ctx_name);
00545 actions.sort();
00546 m_contexts.insert(ctx_name, actions);
00547 }
00548 }
00549
00556 void MythControls::DeleteKey(void)
00557 {
00558 QString context = GetCurrentContext();
00559 QString key = GetCurrentKey();
00560 QString action = GetCurrentAction();
00561
00562 if (context.isEmpty() || key.isEmpty() || action.isEmpty())
00563 {
00564 LOG(VB_GENERAL, LOG_ERR,
00565 "Unable to delete binding, missing information");
00566 return;
00567 }
00568
00569 if (m_bindings->RemoveActionKey(context, action, key))
00570 {
00571 RefreshKeyInformation();
00572 return;
00573 }
00574
00575 QString label = tr("This action is mandatory and needs at least one key "
00576 "bound to it. Instead, try rebinding with another key.");
00577
00578 MythScreenStack *popupStack =
00579 GetMythMainWindow()->GetStack("popup stack");
00580
00581 MythConfirmationDialog *confirmPopup =
00582 new MythConfirmationDialog(popupStack, label, false);
00583
00584 if (confirmPopup->Create())
00585 {
00586 confirmPopup->SetReturnEvent(this, "mandatorydelete");
00587 popupStack->AddScreen(confirmPopup);
00588 }
00589 else
00590 delete confirmPopup;
00591 }
00592
00597 void MythControls::ResolveConflict(ActionID *conflict, int error_level,
00598 const QString &key)
00599 {
00600 if (!conflict)
00601 return;
00602
00603 QString label = tr("This key binding conflicts with %1 in the %2 context.")
00604 .arg(conflict->GetAction()).arg(conflict->GetContext());
00605
00606 bool error = (KeyBindings::kKeyBindingError == error_level);
00607
00608 if (error)
00609 label.append(tr(" Unable to bind key."));
00610 else
00611 label.append(tr(" Do you want to bind it anyway?"));
00612
00613 MythScreenStack *popupStack =
00614 GetMythMainWindow()->GetStack("popup stack");
00615
00616 MythConfirmationDialog *confirmPopup =
00617 new MythConfirmationDialog(popupStack, label, !error);
00618
00619 if (!error)
00620 {
00621 confirmPopup->SetData(qVariantFromValue(key));
00622 confirmPopup->SetReturnEvent(this, "conflict");
00623 }
00624
00625 if (confirmPopup->Create())
00626 popupStack->AddScreen(confirmPopup);
00627
00628 delete conflict;
00629 }
00630
00631 void MythControls::GrabKey(void)
00632 {
00633
00634 MythScreenStack *popupStack =
00635 GetMythMainWindow()->GetStack("popup stack");
00636
00637 KeyGrabPopupBox *keyGrabPopup = new KeyGrabPopupBox(popupStack);
00638
00639 if (keyGrabPopup->Create())
00640 popupStack->AddScreen(keyGrabPopup, false);
00641
00642 connect(keyGrabPopup, SIGNAL(HaveResult(QString)),
00643 SLOT(AddKeyToAction(QString)), Qt::QueuedConnection);
00644 }
00645
00654 void MythControls::AddKeyToAction(QString key, bool ignoreconflict)
00655 {
00656 QString action = GetCurrentAction();
00657 QString context = GetCurrentContext();
00658 QStringList keys = m_bindings->GetActionKeys(context, action);
00659
00660
00661 int binding_index = GetCurrentButton();
00662 if ((binding_index >= (int)Action::kMaximumNumberOfBindings) ||
00663 ((binding_index < keys.size()) && (keys[binding_index] == key)))
00664 {
00665 return;
00666 }
00667
00668 if (!ignoreconflict)
00669 {
00670
00671 int err_level;
00672 ActionID *conflict = m_bindings->GetConflict(context, key, err_level);
00673 if (conflict)
00674 {
00675 ResolveConflict(conflict, err_level, key);
00676
00677 return;
00678 }
00679 }
00680
00681 if (binding_index < keys.count())
00682 m_bindings->ReplaceActionKey(context, action, key,
00683 keys[binding_index]);
00684 else
00685 m_bindings->AddActionKey(context, action, key);
00686
00687 RefreshKeyInformation();
00688 }
00689
00690 void MythControls::customEvent(QEvent *event)
00691 {
00692 if (event->type() == DialogCompletionEvent::kEventType)
00693 {
00694 DialogCompletionEvent *dce = (DialogCompletionEvent*)(event);
00695
00696 QString resultid = dce->GetId();
00697 int buttonnum = dce->GetResult();
00698
00699 if (resultid == "action")
00700 {
00701 if (buttonnum == 0)
00702 GrabKey();
00703 else if (buttonnum == 1)
00704 DeleteKey();
00705 }
00706 else if (resultid == "option")
00707 {
00708 if (buttonnum == 0)
00709 Save();
00710 else if (buttonnum == 1)
00711 ChangeView();
00712 else if (buttonnum == 2)
00713 GetMythMainWindow()->JumpTo("Reset All Keys");
00714 }
00715 else if (resultid == "exit")
00716 {
00717 if (buttonnum == 1)
00718 Save();
00719 else
00720 Teardown();
00721
00722 Close();
00723 }
00724 else if (resultid == "view")
00725 {
00726 QStringList contents;
00727 QString leftcaption, rightcaption;
00728
00729 if (buttonnum == 0)
00730 {
00731 leftcaption = tr("Contexts");
00732 rightcaption = tr("Actions");
00733 m_currentView = kActionsByContext;
00734 contents = m_bindings->GetContexts();
00735 }
00736 else if (buttonnum == 1)
00737 {
00738 leftcaption = tr("Contexts");
00739 rightcaption = tr("Keys");
00740 m_currentView = kKeysByContext;
00741 contents = m_bindings->GetContexts();
00742 }
00743 else if (buttonnum == 2)
00744 {
00745 leftcaption = tr("Keys");
00746 rightcaption = tr("Contexts");
00747 m_currentView = kContextsByKey;
00748 contents = m_bindings->GetKeys();
00749 }
00750 else
00751 return;
00752
00753 m_leftDescription->SetText(leftcaption);
00754 m_rightDescription->SetText(rightcaption);
00755
00756 SetListContents(m_leftList, contents, true);
00757 RefreshKeyInformation();
00758 UpdateRightList();
00759
00760 if (GetFocusWidget() != m_leftList)
00761 SetFocusWidget(m_leftList);
00762 }
00763 else if (resultid == "conflict")
00764 {
00765 if (buttonnum == 1)
00766 {
00767 QString key = dce->GetData().toString();
00768 AddKeyToAction(key, true);
00769 }
00770 }
00771
00772 if (m_menuPopup)
00773 m_menuPopup = NULL;
00774 }
00775
00776 }
00777
00778