MythTV  0.26-pre
mythcontrols.cpp
Go to the documentation of this file.
00001 // -*- Mode: c++ -*-
00029 #include "mythcontrols.h"
00030 
00031 // Qt headers
00032 #include <QStringList>
00033 #include <QCoreApplication>
00034 
00035 // MythTV headers
00036 #include "mythcorecontext.h"
00037 #include "mythmainwindow.h"
00038 
00039 // MythUI headers
00040 #include "mythuitext.h"
00041 #include "mythuibutton.h"
00042 #include "mythuibuttonlist.h"
00043 #include "mythdialogbox.h"
00044 
00045 // MythControls headers
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     // Load the theme for this screen
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     /* start off with the actions by contexts view */
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 // for blank keys, no reason to ask what to do
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         /* prompt user to save changes */
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     // remove all strings from the current list
00312     uilist->Reset();
00313 
00314     // add each new string
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     // get the selected item in the right list.
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(); // Should not happen
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(); // should not happen..
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(); // Should not happen
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     /* create the key bindings and the tree */
00527     m_bindings = new KeyBindings(hostname);
00528     m_sortedContexts = m_bindings->GetContexts();
00529 
00530     /* Alphabetic order, but jump and global at the top  */
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     /* grab a key from the user */
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     // Don't recreating an existing binding...
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         // Check for first of the potential conflicts.
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 /* vim: set expandtab tabstop=4 shiftwidth=4: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends