00001
00029
00030 #include <qnamespace.h>
00031 #include <qstringlist.h>
00032 #include <qapplication.h>
00033 #include <qbuttongroup.h>
00034 #include <qdeepcopy.h>
00035
00036
00037 #include <mythtv/mythcontext.h>
00038
00039
00040 #include "mythcontrols.h"
00041 #include "keygrabber.h"
00042
00043 #define LOC QString("MythControls: ")
00044 #define LOC_ERR QString("MythControls, Error: ")
00045 #define CAPTION_CONTEXT QString("Contexts")
00046 #define CAPTION_ACTION QString("Actions")
00047 #define CAPTION_KEY QString("Keys")
00048
00054 MythControls::MythControls(MythScreenStack *parent, const char *name)
00055 : MythScreenType (parent, name)
00056 {
00057 m_currentView = kActionsByContext;
00058 m_leftList = m_rightList = NULL;
00059 m_description = m_leftDescription = m_rightDescription = NULL;
00060 m_bindings = NULL;
00061
00062 m_contexts.setAutoDelete(true);
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
00095 bool MythControls::Create(void)
00096 {
00097 bool foundtheme = false;
00098
00099
00100 foundtheme = LoadWindowFromXML("controls-ui.xml", "controls", this);
00101
00102 if (!foundtheme)
00103 {
00104 VERBOSE(VB_IMPORTANT, "Unable to load window 'controls' from "
00105 "controls-ui.xml");
00106 return false;
00107 }
00108
00109 m_description = dynamic_cast<MythUIText *>
00110 (GetChild("description"));
00111 m_leftList = dynamic_cast<MythListButton *>
00112 (GetChild("leftlist"));
00113 m_rightList = dynamic_cast<MythListButton *>
00114 (GetChild("rightlist"));
00115 m_leftDescription = dynamic_cast<MythUIText *>
00116 (GetChild("leftdesc"));
00117 m_rightDescription = dynamic_cast<MythUIText *>
00118 (GetChild("rightdesc"));
00119
00120 if (!m_description || !m_leftList || !m_rightList ||
00121 !m_leftDescription || !m_rightDescription)
00122 {
00123 VERBOSE(VB_IMPORTANT, "Theme is missing critical theme elements.");
00124 return false;
00125 }
00126
00127 connect(m_leftList, SIGNAL(itemSelected( MythListButtonItem*)),
00128 this, SLOT( LeftSelected( MythListButtonItem*)));
00129
00130 connect(m_rightList, SIGNAL(itemSelected( MythListButtonItem*)),
00131 this, SLOT( RightSelected(MythListButtonItem*)));
00132 connect(m_rightList, SIGNAL(TakingFocus()),
00133 this, SLOT(RefreshKeyInformation()));
00134
00135 for (uint i = 0; i < Action::kMaximumNumberOfBindings; i++)
00136 {
00137 m_actionButtons.append(dynamic_cast<MythUIButton *>
00138 (GetChild(QString("action_%1").arg(i))));
00139
00140 if (!m_actionButtons.at(i))
00141 {
00142 VERBOSE(VB_IMPORTANT, LOC_ERR +
00143 QString("Unable to load action button action_%1").arg(i));
00144
00145 return false;
00146 }
00147 }
00148
00149 if (!BuildFocusList())
00150 VERBOSE(VB_IMPORTANT, "Failed to build a focuslist. Something is wrong");
00151
00152 SetFocusWidget(m_leftList);
00153 m_leftList->SetCanTakeFocus();
00154 m_leftList->SetActive(true);
00155 m_rightList->SetCanTakeFocus();
00156 m_rightList->SetActive(false);
00157
00158 LoadData(gContext->GetHostName());
00159
00160
00161 m_currentView = kActionsByContext;
00162 SetListContents(m_leftList, m_bindings->GetContexts(), true);
00163 UpdateRightList();
00164
00165 return true;
00166 }
00167
00173 void MythControls::ChangeButtonFocus(int direction)
00174 {
00175 if ((m_leftListType != kContextList) || (m_rightListType != kActionList))
00176 return;
00177
00178 if (direction == 0)
00179 {
00180 SetFocusWidget(m_actionButtons.at(0));
00181 m_rightList->SetActive(false);
00182 return;
00183 }
00184 }
00185
00187 void MythControls::ChangeView(void)
00188 {
00189 QString label = tr("Change View");
00190
00191 MythScreenStack *mainStack =
00192 GetMythMainWindow()->GetMainStack();
00193
00194 m_menuPopup =
00195 new MythDialogBox(label, mainStack, "mcviewmenu");
00196
00197 if (m_menuPopup->Create())
00198 mainStack->AddScreen(m_menuPopup);
00199
00200 m_menuPopup->SetReturnEvent(this, "view");
00201
00202 m_menuPopup->AddButton(tr("Actions By Context"));
00203 m_menuPopup->AddButton(tr("Contexts By Key"));
00204 m_menuPopup->AddButton(tr("Keys By Context"));
00205 m_menuPopup->AddButton(tr("Cancel"));
00206
00207 }
00208
00209 bool MythControls::keyPressEvent(QKeyEvent *event)
00210 {
00211 bool handled = false;
00212 bool escape = false;
00213 QStringList actions;
00214 gContext->GetMainWindow()->TranslateKeyPress("Controls", event, actions);
00215
00216 for (uint i = 0; i < actions.size() && !handled; i++)
00217 {
00218 QString action = actions[i];
00219 handled = true;
00220
00221 if (action == "MENU" || action == "INFO")
00222 {
00223 QString label = tr("Options");
00224
00225 MythScreenStack *mainStack =
00226 GetMythMainWindow()->GetMainStack();
00227
00228 m_menuPopup =
00229 new MythDialogBox(label, mainStack, "optionmenu");
00230
00231 if (m_menuPopup->Create())
00232 mainStack->AddScreen(m_menuPopup);
00233
00234 m_menuPopup->SetReturnEvent(this, "option");
00235
00236 m_menuPopup->AddButton(tr("Save"));
00237 m_menuPopup->AddButton(tr("Change View"));
00238 m_menuPopup->AddButton(tr("Cancel"));
00239 }
00240 else if (action == "SELECT")
00241 {
00242 if (GetFocusWidget() == m_leftList)
00243 NextPrevWidgetFocus(true);
00244 else if (GetFocusWidget() == m_rightList)
00245 {
00246 if (m_currentView == kActionsByContext)
00247 ChangeButtonFocus(0);
00248 else
00249 handled = false;
00250 }
00251 else
00252 {
00253 QString key = GetCurrentKey();
00254 if (!key.isEmpty())
00255 {
00256 QString label = tr("Modify Action");
00257
00258 MythScreenStack *mainStack =
00259 GetMythMainWindow()->GetMainStack();
00260
00261 m_menuPopup =
00262 new MythDialogBox(label, mainStack, "actionmenu");
00263
00264 if (m_menuPopup->Create())
00265 mainStack->AddScreen(m_menuPopup);
00266
00267 m_menuPopup->SetReturnEvent(this, "action");
00268
00269 m_menuPopup->AddButton(tr("Set Binding"));
00270 m_menuPopup->AddButton(tr("Remove Binding"));
00271 m_menuPopup->AddButton(tr("Cancel"));
00272 }
00273 else
00274 AddKeyToAction();
00275 }
00276
00277 }
00278 else if (action == "ESCAPE")
00279 {
00280 escape = true;
00281
00282 handled = false;
00283
00284 if (m_bindings->HasChanges())
00285 {
00286
00287 QString label = tr("Exiting, but there are unsaved changes."
00288 "Which would you prefer?");
00289
00290 MythScreenStack *mainStack =
00291 GetMythMainWindow()->GetMainStack();
00292
00293 m_menuPopup =
00294 new MythDialogBox(label, mainStack, "exitmenu");
00295
00296 if (m_menuPopup->Create())
00297 mainStack->AddScreen(m_menuPopup);
00298
00299 m_menuPopup->SetReturnEvent(this, "exit");
00300
00301 m_menuPopup->AddButton(tr("Save then Exit"));
00302 m_menuPopup->AddButton(tr("Exit without saving changes"));
00303 }
00304 else
00305 GetMythMainWindow()->GetMainStack()->PopScreen();
00306 }
00307 else if (action == "LEFT")
00308 {
00309 NextPrevWidgetFocus(false);
00310 }
00311 else if (action == "RIGHT")
00312 {
00313 NextPrevWidgetFocus(true);
00314 }
00315 else if (GetFocusWidget()->keyPressEvent(event))
00316 {
00317 handled = false;
00318 }
00319 }
00320
00321 return handled;
00322 }
00323
00328 void MythControls::LeftSelected(MythListButtonItem*)
00329 {
00330 UpdateRightList();
00331 }
00332
00337 void MythControls::RightSelected(MythListButtonItem*)
00338 {
00339 RefreshKeyInformation();
00340 }
00341
00342
00348 void MythControls::SetListContents(
00349 MythListButton *uilist, const QStringList &contents, bool arrows)
00350 {
00351
00352 uilist->Reset();
00353
00354
00355 for (size_t i = 0; i < contents.size(); i++)
00356 {
00357 MythListButtonItem *item = new MythListButtonItem(uilist, contents[i]);
00358 item->setDrawArrow(arrows);
00359 }
00360 }
00361
00363 void MythControls::UpdateRightList(void)
00364 {
00365
00366 MythListButtonItem *item = m_leftList->GetItemCurrent();
00367
00368 if (item != NULL)
00369 {
00370 QString rtstr = item->text();
00371
00372 switch(m_currentView)
00373 {
00374 case kActionsByContext:
00375 SetListContents(m_rightList, *(m_contexts[rtstr]));
00376 break;
00377 case kKeysByContext:
00378 SetListContents(m_rightList, m_bindings->GetContextKeys(rtstr));
00379 break;
00380 case kContextsByKey:
00381 SetListContents(m_rightList, m_bindings->GetKeyContexts(rtstr));
00382 break;
00383 }
00384 }
00385 else
00386 VERBOSE(VB_IMPORTANT, QString("Left List Returned Null!"));
00387 }
00388
00393 void MythControls::RefreshKeyInformation(void)
00394 {
00395 for (uint i = 0; i < Action::kMaximumNumberOfBindings; i++)
00396 m_actionButtons.at(i)->SetText("");
00397
00398 if (GetFocusWidget() == m_leftList)
00399 {
00400 m_description->SetText("");
00401 return;
00402 }
00403
00404 const QString context = GetCurrentContext();
00405 const QString action = GetCurrentAction();
00406
00407 QString desc = m_bindings->GetActionDescription(context, action);
00408 m_description->SetText(desc);
00409
00410 QStringList keys = m_bindings->GetActionKeys(context, action);
00411 for (uint i = 0; (i < keys.count()) &&
00412 (i < Action::kMaximumNumberOfBindings); i++)
00413 {
00414 m_actionButtons.at(i)->SetText(keys[i]);
00415 }
00416 }
00417
00418
00425 QString MythControls::GetCurrentContext(void)
00426 {
00427 if (m_leftListType == kContextList)
00428 return m_leftList->GetItemCurrent()->text();
00429
00430 if (GetFocusWidget() == m_leftList)
00431 return QString::null;
00432
00433 QString desc = m_rightList->GetItemCurrent()->text();
00434 int loc = desc.find(" => ");
00435 if (loc == -1)
00436 return QString::null;
00437
00438 if (m_rightListType == kContextList)
00439 return desc.left(loc);
00440
00441 return desc.mid(loc + 4);
00442 }
00443
00450 QString MythControls::GetCurrentAction(void)
00451 {
00452 if (m_leftListType == kActionList)
00453 {
00454 if (m_leftList && m_leftList->GetItemCurrent())
00455 return QDeepCopy<QString>(m_leftList->GetItemCurrent()->text());
00456 return QString::null;
00457 }
00458
00459 if (GetFocusWidget() == m_leftList)
00460 return QString::null;
00461
00462 if (!m_rightList || !m_rightList->GetItemCurrent())
00463 return QString::null;
00464
00465 QString desc = m_rightList->GetItemCurrent()->text();
00466 if (m_leftListType == kContextList && m_rightListType == kActionList)
00467 return QDeepCopy<QString>(desc);
00468
00469 int loc = desc.find(" => ");
00470 if (loc == -1)
00471 return QString::null;
00472
00473 if (m_rightListType == kActionList)
00474 return desc.left(loc);
00475
00476 QString rv = desc.mid(loc+4);
00477 if (rv == "<none>")
00478 return QString::null;
00479
00480 return rv;
00481 }
00482
00486 uint MythControls::GetCurrentButton(void)
00487 {
00488 for (uint i = 0; i < Action::kMaximumNumberOfBindings; i++)
00489 {
00490 MythUIButton *button = m_actionButtons.at(i);
00491 MythUIType *uitype = GetFocusWidget();
00492 if (uitype == button)
00493 return i;
00494 }
00495
00496 return Action::kMaximumNumberOfBindings;
00497 }
00498
00505 QString MythControls::GetCurrentKey(void)
00506 {
00507 if (m_leftListType == kKeyList)
00508 return m_leftList->GetItemCurrent()->text();
00509
00510 if (GetFocusWidget() == m_leftList)
00511 return QString::null;
00512
00513 if ((m_leftListType == kContextList) && (m_rightListType == kActionList))
00514 {
00515 QString context = GetCurrentContext();
00516 QString action = GetCurrentAction();
00517 uint b = GetCurrentButton();
00518 QStringList keys = m_bindings->GetActionKeys(context, action);
00519
00520 if (b < keys.count())
00521 return keys[b];
00522
00523 return QString::null;
00524 }
00525
00526 QString desc = m_rightList->GetItemCurrent()->text();
00527 int loc = desc.find(" => ");
00528 if (loc == -1)
00529 return QString::null;
00530
00531
00532 if (m_rightListType == kKeyList)
00533 return desc.left(loc);
00534
00535 return desc.mid(loc + 4);
00536 }
00537
00542 void MythControls::LoadData(const QString &hostname)
00543 {
00544
00545 m_bindings = new KeyBindings(hostname);
00546 m_sortedContexts = m_bindings->GetContexts();
00547
00548
00549 m_sortedContexts.sort();
00550 m_sortedContexts.remove(ActionSet::kJumpContext);
00551 m_sortedContexts.remove(ActionSet::kGlobalContext);
00552 m_sortedContexts.insert(m_sortedContexts.begin(), 1,
00553 ActionSet::kGlobalContext);
00554 m_sortedContexts.insert(m_sortedContexts.begin(), 1,
00555 ActionSet::kJumpContext);
00556
00557 QStringList actions;
00558 for (uint i = 0; i < m_sortedContexts.size(); i++)
00559 {
00560 actions = m_bindings->GetActions(m_sortedContexts[i]);
00561 actions.sort();
00562 m_contexts.insert(m_sortedContexts[i], new QStringList(actions));
00563 }
00564 }
00565
00572 void MythControls::DeleteKey(void)
00573 {
00574 QString context = GetCurrentContext();
00575 QString key = GetCurrentKey();
00576 QString action = GetCurrentAction();
00577 QString ptitle = tr("Manditory Action");
00578 QString pdesc =
00579 tr("This action is manditory and needs at least one key "
00580 "bound to it. Instead, try rebinding with another key.");
00581
00582 if (context.isEmpty() || key.isEmpty() || action.isEmpty())
00583 {
00584 MythPopupBox::showOkPopup(gContext->GetMainWindow(), ptitle, pdesc);
00585 return;
00586 }
00587
00588 bool ok = MythPopupBox::showOkCancelPopup(
00589 gContext->GetMainWindow(), "confirmdelete",
00590 tr("Delete this binding?"), true);
00591
00592 if (!ok)
00593 return;
00594
00595 if (!m_bindings->RemoveActionKey(context, action, key))
00596 {
00597 MythPopupBox::showOkPopup(gContext->GetMainWindow(), ptitle, pdesc);
00598 return;
00599 }
00600
00601 RefreshKeyInformation();
00602 }
00603
00608 bool MythControls::ResolveConflict(ActionID *conflict, int error_level)
00609 {
00610 if (!conflict)
00611 return false;
00612
00613 QString msg = tr("This key binding conflicts with %1 in the %2 context.")
00614 .arg(conflict->GetAction()).arg(conflict->GetContext());
00615
00616 if (KeyBindings::kKeyBindingError == error_level)
00617 {
00618 MythPopupBox::showOkPopup(
00619 gContext->GetMainWindow(), tr("Conflicting Binding"), msg);
00620
00621 return false;
00622 }
00623
00624 msg = tr("This key binding may conflict with %1 in the %2 context. "
00625 "Do you want to bind it anyway?")
00626 .arg(conflict->GetAction()).arg(conflict->GetContext());
00627
00628 DialogCode res = MythPopupBox::Show2ButtonPopup(
00629 gContext->GetMainWindow(), tr("Conflict Warning"),
00630 msg, tr("Bind Key"), QObject::tr("Cancel"), kDialogCodeButton1);
00631
00632 return (kDialogCodeButton0 == res);
00633 }
00634
00643 void MythControls::AddKeyToAction(void)
00644 {
00645
00646 KeyGrabPopupBox *getkey = new KeyGrabPopupBox(gContext->GetMainWindow());
00647 DialogCode code = getkey->ExecPopup();
00648 QString key = getkey->GetCapturedKey();
00649 getkey->deleteLater();
00650 getkey = NULL;
00651
00652 if (kDialogCodeRejected == code)
00653 return;
00654
00655 QString action = GetCurrentAction();
00656 QString context = GetCurrentContext();
00657 QStringList keys = m_bindings->GetActionKeys(context, action);
00658
00659
00660 uint binding_index = GetCurrentButton();
00661 if ((binding_index >= Action::kMaximumNumberOfBindings) ||
00662 (keys[binding_index] == key))
00663 {
00664 return;
00665 }
00666
00667
00668 int err_level;
00669 ActionID *conflict = m_bindings->GetConflict(context, key, err_level);
00670 if (conflict)
00671 {
00672 bool ok = ResolveConflict(conflict, err_level);
00673
00674 delete conflict;
00675
00676 if (!ok)
00677 return;
00678 }
00679
00680 if (binding_index < keys.count())
00681 {
00682 VERBOSE(VB_IMPORTANT, "ReplaceActionKey");
00683 m_bindings->ReplaceActionKey(context, action, key,
00684 keys[binding_index]);
00685 }
00686 else
00687 {
00688 VERBOSE(VB_IMPORTANT, "AddActionKey");
00689 m_bindings->AddActionKey(context, action, key);
00690 }
00691
00692 RefreshKeyInformation();
00693 }
00694
00695 void MythControls::customEvent(QCustomEvent *event)
00696 {
00697
00698 if (event->type() == kMythDialogBoxCompletionEventType)
00699 {
00700 DialogCompletionEvent *dce =
00701 dynamic_cast<DialogCompletionEvent*>(event);
00702
00703 QString resultid= dce->GetId();
00704 int buttonnum = dce->GetResult();
00705
00706 if (resultid == "action")
00707 {
00708 if (buttonnum == 0)
00709 AddKeyToAction();
00710 else if (buttonnum == 1)
00711 DeleteKey();
00712 }
00713 else if (resultid == "option")
00714 {
00715 if (buttonnum == 0)
00716 Save();
00717 else if (buttonnum == 1)
00718 ChangeView();
00719 }
00720 else if (resultid == "exit")
00721 {
00722 if (buttonnum == 0)
00723 Save();
00724
00725 GetMythMainWindow()->GetMainStack()->PopScreen();
00726 }
00727 else if (resultid == "view")
00728 {
00729 QStringList contents;
00730 QString leftcaption, rightcaption;
00731
00732 if (buttonnum == 0)
00733 {
00734 leftcaption = tr(CAPTION_CONTEXT);
00735 rightcaption = tr(CAPTION_ACTION);
00736 m_currentView = kActionsByContext;
00737 contents = m_bindings->GetContexts();
00738 }
00739 else if (buttonnum == 1)
00740 {
00741 leftcaption = tr(CAPTION_CONTEXT);
00742 rightcaption = tr(CAPTION_KEY);
00743 m_currentView = kKeysByContext;
00744 contents = m_bindings->GetContexts();
00745 }
00746 else if (buttonnum == 2)
00747 {
00748 leftcaption = tr(CAPTION_KEY);
00749 rightcaption = tr(CAPTION_CONTEXT);
00750 m_currentView = kContextsByKey;
00751 contents = m_bindings->GetKeys();
00752 }
00753 else
00754 return;
00755
00756 m_leftDescription->SetText(leftcaption);
00757 m_rightDescription->SetText(rightcaption);
00758
00759 SetListContents(m_leftList, contents, true);
00760 RefreshKeyInformation();
00761 UpdateRightList();
00762
00763 if (GetFocusWidget() != m_leftList)
00764 SetFocusWidget(m_leftList);
00765 }
00766
00767 m_menuPopup = NULL;
00768 }
00769
00770 }
00771
00772