Ticket #9613: multikeybindings_v8.patch

File multikeybindings_v8.patch, 27.6 KB (added by Jim Stichnoth <stichnot@…>, 9 years ago)

Add Multikeydata::clear() and call it in MythMainWindow::ClearKeyContext?().

  • mythtv/libs/libmythtv/dbcheck.cpp

    diff --git a/mythtv/libs/libmythtv/dbcheck.cpp b/mythtv/libs/libmythtv/dbcheck.cpp
    index c9570d5..7ae8e25 100644
    a b tmp.constData(), 
    61666166"  description varchar(255) default NULL,"
    61676167"  keylist varchar(128) default NULL,"
    61686168"  hostname varchar(64) NOT NULL default '',"
     6169"  multikey varchar(128) default NULL,"
    61696170"  PRIMARY KEY  (destination,hostname)"
    61706171");",
    61716172"CREATE TABLE keybindings ("
    tmp.constData(), 
    61746175"  description varchar(255) default NULL,"
    61756176"  keylist varchar(128) default NULL,"
    61766177"  hostname varchar(64) NOT NULL default '',"
     6178"  multikey varchar(128) default NULL,"
    61776179"  PRIMARY KEY  (`context`,`action`,hostname)"
    61786180");",
    61796181"CREATE TABLE keyword ("
  • mythtv/libs/libmythtv/tv_play.cpp

    diff --git a/mythtv/libs/libmythtv/tv_play.cpp b/mythtv/libs/libmythtv/tv_play.cpp
    index 3f6d17d..d5c3a88 100644
    a b TV::TV(void) 
    855855      endOfPlaybackTimerId(0),      embedCheckTimerId(0),
    856856      endOfRecPromptTimerId(0),     videoExitDialogTimerId(0),
    857857      pseudoChangeChanTimerId(0),   speedChangeTimerId(0),
    858       errorRecoveryTimerId(0),      exitPlayerTimerId(0)
     858      errorRecoveryTimerId(0),      exitPlayerTimerId(0),
     859      multikeyContext(new MultikeyContext())
    859860{
    860861    memset(&ddMapLoader, 0, sizeof(pthread_t));
    861862    LOG(VB_GENERAL, LOG_INFO, LOC + "Creating TV object");
    TV::~TV(void) 
    11961197    }
    11971198    ReturnPlayerLock(mctx);
    11981199
     1200    if (multikeyContext)
     1201    {
     1202        delete multikeyContext;
     1203        multikeyContext = NULL;
     1204    }
     1205
    11991206    LOG(VB_PLAYBACK, LOG_INFO, "TV::~TV() -- end");
    12001207}
    12011208
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    33383345    if (ignoreKeys)
    33393346    {
    33403347        handled = GetMythMainWindow()->TranslateKeyPress(
    3341                   "TV Playback", e, actions);
     3348                  "TV Playback", e, actions, true, multikeyContext);
    33423349
    33433350        if (handled || actions.isEmpty())
     3351        {
     3352            CommitMultikey(actx);
    33443353            return true;
     3354        }
    33453355
    33463356        bool esc   = has_action("ESCAPE", actions) ||
    33473357                     has_action("BACK", actions);
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    33493359        bool play  = has_action(ACTION_PLAY,  actions);
    33503360
    33513361        if ((!esc || browsehelper->IsBrowsing()) && !pause && !play)
     3362        {
     3363            CommitMultikey(actx);
    33523364            return false;
     3365        }
    33533366    }
    33543367
    33553368    OSD *osd = GetOSDLock(actx);
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    33633376    if (editmode && !handled)
    33643377    {
    33653378        handled |= GetMythMainWindow()->TranslateKeyPress(
    3366                    "TV Editing", e, actions);
     3379                   "TV Editing", e, actions, true, multikeyContext);
    33673380
    33683381        if (!handled)
    33693382        {
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    34073420    }
    34083421
    34093422    if (handled)
     3423    {
     3424        CommitMultikey(actx);
    34103425        return true;
     3426    }
    34113427
    34123428    // If text is already queued up, be more lax on what is ok.
    34133429    // This allows hex teletext entry and minor channel entry.
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    34193435        if (ok || txt=="_" || txt=="-" || txt=="#" || txt==".")
    34203436        {
    34213437            AddKeyToInputQueue(actx, txt.at(0).toLatin1());
     3438            CommitMultikey(actx, false);
    34223439            return true;
    34233440        }
    34243441    }
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    34293446    {
    34303447        QStringList tt_actions;
    34313448        handled = GetMythMainWindow()->TranslateKeyPress(
    3432                   "Teletext Menu", e, tt_actions);
     3449                  "Teletext Menu", e, tt_actions, true, multikeyContext);
    34333450
    34343451        if (!handled && !tt_actions.isEmpty())
    34353452        {
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    34383455                if (actx->player->HandleTeletextAction(tt_actions[i]))
    34393456                {
    34403457                    actx->UnlockDeletePlayer(__FILE__, __LINE__);
     3458                    CommitMultikey(actx);
    34413459                    return true;
    34423460                }
    34433461            }
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    34493467    {
    34503468        QStringList itv_actions;
    34513469        handled = GetMythMainWindow()->TranslateKeyPress(
    3452                   "TV Playback", e, itv_actions);
     3470                  "TV Playback", e, itv_actions, true, multikeyContext);
    34533471
    34543472        if (!handled && !itv_actions.isEmpty())
    34553473        {
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    34583476                if (actx->player->ITVHandleAction(itv_actions[i]))
    34593477                {
    34603478                    actx->UnlockDeletePlayer(__FILE__, __LINE__);
     3479                    CommitMultikey(actx);
    34613480                    return true;
    34623481                }
    34633482            }
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    34663485    actx->UnlockDeletePlayer(__FILE__, __LINE__);
    34673486
    34683487    handled = GetMythMainWindow()->TranslateKeyPress(
    3469               "TV Playback", e, actions);
     3488              "TV Playback", e, actions, true, multikeyContext);
    34703489
    34713490    if (handled || actions.isEmpty())
     3491    {
     3492        CommitMultikey(actx);
    34723493        return true;
     3494    }
    34733495
    34743496    handled = false;
    34753497
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    34963518#endif // DEBUG_ACTIONS
    34973519
    34983520    if (handled)
     3521    {
     3522        CommitMultikey(actx);
    34993523        return true;
     3524    }
    35003525
    35013526    if (!handled)
    35023527    {
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    35103535            {
    35113536                AddKeyToInputQueue(actx, '0' + val);
    35123537                handled = true;
     3538                CommitMultikey(actx, false);
    35133539            }
    35143540        }
    35153541    }
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    35173543    return true;
    35183544}
    35193545
     3546void TV::CommitMultikey(PlayerContext *ctx, bool updateOSD)
     3547{
     3548    MultikeyContext::MultikeyStatus status;
     3549    QString prefix;
     3550    multikeyContext->Commit(status, prefix);
     3551    bool isMultikey = (QKeySequence(prefix).count() > 1);
     3552    if (updateOSD)
     3553    {
     3554        switch (status)
     3555        {
     3556        case MultikeyContext::kMKnoPrefix:
     3557            // clear the OSD text
     3558            HideOSDWindow(ctx, "osd_input");
     3559            break;
     3560        case MultikeyContext::kMKpartialMatch:
     3561            SetOSDText(ctx, "osd_input", "osd_number_entry",
     3562                       prefix, kOSDTimeout_Med);
     3563            break;
     3564        case MultikeyContext::kMKfullMatch:
     3565            // same as partial match but with short timeout
     3566            if (isMultikey)
     3567                SetOSDText(ctx, "osd_input", "osd_number_entry",
     3568                           prefix, kOSDTimeout_Short);
     3569            break;
     3570        case MultikeyContext::kMKbadMatch:
     3571            // same as full match but with error text
     3572            SetOSDText(ctx, "osd_input", "osd_number_entry",
     3573                       prefix + ": " + QObject::tr("Unknown key sequence"),
     3574                       kOSDTimeout_Short);
     3575            break;
     3576        }
     3577    }
     3578}
     3579
    35203580bool TV::BrowseHandleAction(PlayerContext *ctx, const QStringList &actions)
    35213581{
    35223582    if (!browsehelper->IsBrowsing())
  • mythtv/libs/libmythtv/tv_play.h

    diff --git a/mythtv/libs/libmythtv/tv_play.h b/mythtv/libs/libmythtv/tv_play.h
    index e8f90e7..4d4020b 100644
    a b using namespace std; 
    4444
    4545class QDateTime;
    4646class OSD;
     47class MultikeyContext;
    4748class RemoteEncoder;
    4849class MythPlayer;
    4950class DetectLetterbox;
    class MTV_PUBLIC TV : public QObject 
    358359    void ClearInputQueues(const PlayerContext*, bool hideosd);
    359360    bool CommitQueuedInput(PlayerContext*);
    360361    bool ProcessSmartChannel(const PlayerContext*, QString&);
     362    void CommitMultikey(PlayerContext*, bool updateOSD=true);
    361363
    362364    // query key queues
    363365    bool HasQueuedInput(void) const
    class MTV_PUBLIC TV : public QObject 
    824826    TimerContextMap      signalMonitorTimerId;
    825827    TimerContextMap      tvchainUpdateTimerId;
    826828
     829    MultikeyContext     *multikeyContext;
     830
    827831  public:
    828832    // Constants
    829833    static const int kInitFFRWSpeed; ///< 1x, default to normal speed
  • mythtv/libs/libmythui/mythmainwindow.cpp

    diff --git a/mythtv/libs/libmythui/mythmainwindow.cpp b/mythtv/libs/libmythui/mythmainwindow.cpp
    index 833a501..5d10747 100644
    a b using namespace std; 
    8080
    8181#define LOC      QString("MythMainWindow: ")
    8282
     83class MultikeyData
     84{
     85public:
     86    void AddMultikeyMapping(const QString &multikey, QString action)
     87    {
     88        QKeySequence seq(multikey);
     89        switch (seq.count())
     90        {
     91        case 4:
     92            ++multikeyPrefixes[QKeySequence(seq[0], seq[1], seq[2])];
     93            // fallthrough
     94        case 3:
     95            ++multikeyPrefixes[QKeySequence(seq[0], seq[1])];
     96            // fallthrough
     97        case 2:
     98            ++multikeyPrefixes[QKeySequence(seq[0])];
     99            // fallthrough
     100        case 1:
     101            multikeyActionMap[seq].append(action);
     102        }
     103    }
     104
     105    bool IsMultikeyPrefix(const QString &multikey)
     106    {
     107        return multikeyPrefixes.count(QKeySequence(multikey));
     108    }
     109
     110    bool GetMultikeyMapping(const QString &multikey, QStringList &actions)
     111    {
     112        QKeySequence seq(multikey);
     113        if (multikeyActionMap.count(seq) > 0)
     114        {
     115            actions += multikeyActionMap[seq];
     116            return true;
     117        }
     118        return false;
     119    }
     120
     121    void clear(void)
     122    {
     123        multikeyActionMap.clear();
     124        multikeyPrefixes.clear();
     125    }
     126
     127private:
     128    QMap<QKeySequence, QStringList> multikeyActionMap;
     129    QMap<QKeySequence, int> multikeyPrefixes;
     130};
     131
    83132class KeyContext
    84133{
    85134  public:
    86135    void AddMapping(int key, QString action)
    87136    {
    88137        actionMap[key].append(action);
     138        multiData.AddMultikeyMapping(QKeySequence(key).toString(), action);
    89139    }
    90140
    91141    bool GetMapping(int key, QStringList &actions)
    class KeyContext 
    99149    }
    100150
    101151    QMap<int, QStringList> actionMap;
     152    MultikeyData multiData;
    102153};
    103154
    104155struct JumpData
    class MythMainWindowPrivate 
    210261    QHash<QString, KeyContext *> keyContexts;
    211262    QMap<int, JumpData*> jumpMap;
    212263    QMap<QString, JumpData> destinationMap;
     264    MultikeyData jumpMultikeyData;
    213265    QMap<QString, MPData> mediaPluginMap;
    214266
    215267    void (*exitmenucallback)(void);
    class MythMainWindowPrivate 
    258310    bool m_pendingUpdate;
    259311};
    260312
     313// Returns false if the key couldn't be added (prefix too long).
     314bool MultikeyContext::AddKey(int key)
     315{
     316    currentPrefix = oldPrefix;
     317    bool result = true;
     318    QKeySequence seq(currentPrefix);
     319    int k1 = seq[0];
     320    int k2 = seq[1];
     321    int k3 = seq[2];
     322    int k4 = seq[3];
     323    if (k1 == 0)
     324        k1 = key;
     325    else if (k2 == 0)
     326        k2 = key;
     327    else if (k3 == 0)
     328        k3 = key;
     329    else if (k4 == 0)
     330        k4 = key;
     331    else
     332        result = false;
     333    if (result)
     334        currentPrefix = QKeySequence(k1, k2, k3, k4).toString();
     335    return result;
     336}
     337
    261338// Make keynum in QKeyEvent be equivalent to what's in QKeySequence
    262339int MythMainWindowPrivate::TranslateKeyNum(QKeyEvent* e)
    263340{
    void MythMainWindow::ExitToMainMenu(void) 
    14341511 * \param e       The keypress event to lookup.
    14351512 * \param actions The QStringList that will contain the list of actions.
    14361513 * \param allowJumps if true then jump points are allowed
     1514 * \param prefixContext if non-NULL then multi-key bindings are allowed.
    14371515 *
    14381516 * \return true if the key event has been handled (the keypress was a jumpoint)
    14391517           false if the caller should continue to handle keypress
    14401518 */
    14411519bool MythMainWindow::TranslateKeyPress(const QString &context,
    14421520                                       QKeyEvent *e, QStringList &actions,
    1443                                        bool allowJumps)
     1521                                       bool allowJumps,
     1522                                       MultikeyContext *prefixContext)
    14441523{
    14451524    actions.clear();
    14461525
    bool MythMainWindow::TranslateKeyPress(const QString &context, 
    14721551    }
    14731552
    14741553    int keynum = d->TranslateKeyNum(e);
    1475 
    1476     QStringList localActions;
    1477     if (allowJumps && (d->jumpMap.count(keynum) > 0) &&
    1478         (!d->jumpMap[keynum]->localAction.isEmpty()) &&
    1479         (d->keyContexts.value(context)) &&
    1480         (d->keyContexts.value(context)->GetMapping(keynum, localActions)))
    1481     {
    1482         if (localActions.contains(d->jumpMap[keynum]->localAction))
    1483             allowJumps = false;
     1554    bool checkOnlyJumpPoints = false;
     1555    bool wasEmpty = prefixContext ? prefixContext->GetPrefix().isEmpty() : true;
     1556    bool noOverflow = prefixContext ? prefixContext->AddKey(keynum) : true;
     1557    const QString singleKey = QKeySequence(keynum).toString();
     1558    const QString prefix = prefixContext ? prefixContext->GetPrefix() : singleKey;
     1559    MultikeyContext::MultikeyStatus status;
     1560    JumpData *singleKeyJump = NULL, *multiKeyJump = NULL;
     1561        // There is a potential race condition where an input timeout
     1562        // thread resets the context at the same time this code is
     1563        // operating on it.
     1564    QStringList singleKeyJumpActions, multiKeyJumpActions;
     1565    QStringList singleKeyActions, multiKeyActions;
     1566    KeyContext *kctx = d->keyContexts.value(context);
     1567    KeyContext *gctx = d->keyContexts.value("Global");
     1568
     1569    if (allowJumps)
     1570    {
     1571        // Find single-key jumppoint bindings.  This is only used when
     1572        // a multi-key sequence (regular or jumppoint) is aborted
     1573        // mid-sequence with a single-key jump point.
     1574        if (d->jumpMultikeyData.GetMultikeyMapping(singleKey, singleKeyJumpActions))
     1575            singleKeyJump = &d->destinationMap[singleKeyJumpActions[0]];
     1576        // Find multi-key jumppoint bindings.
     1577        if (noOverflow && d->jumpMultikeyData.GetMultikeyMapping(prefix, multiKeyJumpActions))
     1578            multiKeyJump = &d->destinationMap[multiKeyJumpActions[0]];
     1579    }
     1580    // Find regular bindings (both single- and multi-key) in the given
     1581    // and global contexts.
     1582    if (kctx)
     1583    {
     1584        if (noOverflow)
     1585            kctx->multiData.GetMultikeyMapping(prefix, multiKeyActions);
     1586        kctx->multiData.GetMultikeyMapping(singleKey, singleKeyActions);
     1587    }
     1588    if (gctx && context != "Global")
     1589    {
     1590        if (noOverflow)
     1591            gctx->multiData.GetMultikeyMapping(prefix, multiKeyActions);
     1592        gctx->multiData.GetMultikeyMapping(singleKey, singleKeyActions);
     1593    }
     1594
     1595    // Disable the jump point if there is a corresponding local action
     1596    // for the key sequence.
     1597    if (multiKeyJump &&
     1598        !multiKeyJump->localAction.isEmpty() &&
     1599        multiKeyActions.contains(multiKeyJump->localAction))
     1600    {
     1601        multiKeyJump = singleKeyJump = NULL;
     1602    }
     1603    if (singleKeyJump &&
     1604        !singleKeyJump->localAction.isEmpty() &&
     1605        singleKeyActions.contains(singleKeyJump->localAction))
     1606    {
     1607        multiKeyJump = singleKeyJump = NULL;
     1608        multiKeyActions = singleKeyActions;
     1609    }
     1610
     1611    // Execute the jump point if there is one.
     1612    if (multiKeyJump || (singleKeyJump && multiKeyActions.isEmpty()))
     1613    {
     1614        JumpData *jumpEntry = multiKeyJump ? multiKeyJump : singleKeyJump;
     1615        if (!jumpEntry->exittomain && d->exitmenucallback == NULL)
     1616        {
     1617            if (prefixContext)
     1618                prefixContext->SetStatus(MultikeyContext::kMKfullMatch);
     1619            void (*callback)(void) = jumpEntry->callback;
     1620            callback();
     1621            return true;
     1622        }
     1623        if (d->exitmenucallback == NULL)
     1624        {
     1625            if (prefixContext)
     1626                prefixContext->SetStatus(MultikeyContext::kMKfullMatch);
     1627            d->exitingtomain = true;
     1628            d->exitmenucallback = jumpEntry->callback;
     1629            QCoreApplication::postEvent(
     1630                this, new QEvent(MythEvent::kExitToMainMenuEventType));
     1631            return true;
     1632        }
    14841633    }
    14851634
    1486     if (allowJumps && d->jumpMap.count(keynum) > 0 &&
    1487             !d->jumpMap[keynum]->exittomain && d->exitmenucallback == NULL)
     1635    // Handle a regular action if there is one.
     1636    if (!multiKeyActions.isEmpty())
    14881637    {
    1489         void (*callback)(void) = d->jumpMap[keynum]->callback;
    1490         callback();
    1491         return true;
     1638        if (prefixContext)
     1639            prefixContext->SetStatus(MultikeyContext::kMKfullMatch);
     1640        actions = multiKeyActions;
     1641        return false;
    14921642    }
    14931643
    1494     if (allowJumps &&
    1495         d->jumpMap.count(keynum) > 0 && d->exitmenucallback == NULL)
     1644    // No full match, so handle partial matches and bad matches.
     1645    if (noOverflow)
    14961646    {
    1497         d->exitingtomain = true;
    1498         d->exitmenucallback = d->jumpMap[keynum]->callback;
    1499         QCoreApplication::postEvent(
    1500             this, new QEvent(MythEvent::kExitToMainMenuEventType));
    1501         return true;
     1647        // Check for a partial match.
     1648        if ((kctx && kctx->multiData.IsMultikeyPrefix(prefix)) ||
     1649            (gctx && context != "Global" && gctx->multiData.IsMultikeyPrefix(prefix)) ||
     1650            (d->jumpMultikeyData.IsMultikeyPrefix(prefix)))
     1651        {
     1652            if (prefixContext)
     1653                prefixContext->SetStatus(MultikeyContext::kMKpartialMatch);
     1654            return false;
     1655        }
    15021656    }
    1503 
    1504     if (d->keyContexts.value(context))
    1505         d->keyContexts.value(context)->GetMapping(keynum, actions);
    1506 
    1507     if (context != "Global")
    1508         d->keyContexts.value("Global")->GetMapping(keynum, actions);
    1509 
     1657    if (prefixContext)
     1658        prefixContext->SetStatus(wasEmpty ? MultikeyContext::kMKnoPrefix :
     1659                                 MultikeyContext::kMKbadMatch);
    15101660    return false;
    15111661}
    15121662
    void MythMainWindow::ClearKeyContext(const QString &context) 
    15311681{
    15321682    KeyContext *keycontext = d->keyContexts.value(context);
    15331683    if (keycontext != NULL)
     1684    {
    15341685        keycontext->actionMap.clear();
     1686        keycontext->multiData.clear();
     1687    }
    15351688}
    15361689
    15371690void MythMainWindow::BindKey(const QString &context, const QString &action,
    1538                              const QString &key)
     1691                             const QString &key, const QString &multikey)
    15391692{
    15401693    QKeySequence keyseq(key);
    15411694
    void MythMainWindow::BindKey(const QString &context, const QString &action, 
    15641717        if (action == "ESCAPE" && context == "Global" && i == 0)
    15651718            d->escapekey = keynum;
    15661719    }
     1720
     1721    QStringList multidummyaction("");
     1722    if (d->keyContexts.value(context)->multiData.GetMultikeyMapping(multikey, multidummyaction))
     1723    {
     1724        LOG(VB_GENERAL, LOG_INFO, QString("Multikey %1 is bound to multiple actions "
     1725                                    "in context %2.")
     1726                .arg(multikey).arg(context));
     1727    }
     1728    d->keyContexts.value(context)->multiData.AddMultikeyMapping(multikey, action);
    15671729}
    15681730
    15691731void MythMainWindow::RegisterKey(const QString &context, const QString &action,
    15701732                                 const QString &description, const QString &key)
    15711733{
    15721734    QString keybind = key;
     1735    QString multikeybind = "";
    15731736
    15741737    MSqlQuery query(MSqlQuery::InitCon());
    15751738
    15761739    if (d->m_useDB && query.isConnected())
    15771740    {
    1578         query.prepare("SELECT keylist, description FROM keybindings WHERE "
     1741        query.prepare("SELECT keylist, description, multikey FROM keybindings WHERE "
    15791742                      "context = :CONTEXT AND action = :ACTION AND "
    15801743                      "hostname = :HOSTNAME ;");
    15811744        query.bindValue(":CONTEXT", context);
    void MythMainWindow::RegisterKey(const QString &context, const QString &action, 
    15861749        {
    15871750            keybind = query.value(0).toString();
    15881751            QString db_description = query.value(1).toString();
     1752            multikeybind = query.value(2).toString();
    15891753
    15901754            // Update keybinding description if changed
    15911755            if (db_description != description)
    void MythMainWindow::RegisterKey(const QString &context, const QString &action, 
    16131777        else
    16141778        {
    16151779            QString inskey = keybind;
     1780            QString insmultikey = multikeybind;
    16161781
    16171782            query.prepare("INSERT INTO keybindings (context, action, "
    1618                           "description, keylist, hostname) VALUES "
     1783                          "description, keylist, hostname, multikey) VALUES "
    16191784                          "( :CONTEXT, :ACTION, :DESCRIPTION, :KEYLIST, "
    1620                           ":HOSTNAME );");
     1785                          ":HOSTNAME, :MULTIKEY );");
    16211786            query.bindValue(":CONTEXT", context);
    16221787            query.bindValue(":ACTION", action);
    16231788            query.bindValue(":DESCRIPTION", description);
    16241789            query.bindValue(":KEYLIST", inskey);
    16251790            query.bindValue(":HOSTNAME", GetMythDB()->GetHostName());
     1791            query.bindValue(":MULTIKEY", insmultikey);
    16261792
    16271793            if (!query.exec() && !(GetMythDB()->SuppressDBMessages()))
    16281794            {
    void MythMainWindow::RegisterKey(const QString &context, const QString &action, 
    16311797        }
    16321798    }
    16331799
    1634     BindKey(context, action, keybind);
     1800    BindKey(context, action, keybind, multikeybind);
    16351801}
    16361802
    16371803QString MythMainWindow::GetKey(const QString &context,
    void MythMainWindow::ClearJump(const QString &destination) 
    16771843}
    16781844
    16791845
    1680 void MythMainWindow::BindJump(const QString &destination, const QString &key)
     1846void MythMainWindow::BindJump(const QString &destination, const QString &key,
     1847                              const QString &multikey)
    16811848{
    16821849    /* make sure the jump point exists */
    16831850    if (d->destinationMap.find(destination) == d->destinationMap.end())
    void MythMainWindow::BindJump(const QString &destination, const QString &key) 
    17021869#endif
    17031870
    17041871            d->jumpMap[keynum] = &d->destinationMap[destination];
     1872            d->jumpMultikeyData.AddMultikeyMapping(QKeySequence(keynum).toString(), destination);
    17051873        }
    17061874        else
    17071875        {
    void MythMainWindow::BindJump(const QString &destination, const QString &key) 
    17141882        LOG(VB_GENERAL, LOG_DEBUG,
    17151883            QString("JumpPoint: %2 exists, no keybinding") .arg(destination));
    17161884#endif
     1885
     1886    d->jumpMultikeyData.AddMultikeyMapping(multikey, destination);
    17171887}
    17181888
    17191889void MythMainWindow::RegisterJump(const QString &destination,
    void MythMainWindow::RegisterJump(const QString &destination, 
    17221892                                  bool exittomain, QString localAction)
    17231893{
    17241894    QString keybind = key;
     1895    QString multikeybind = "";
    17251896
    17261897    MSqlQuery query(MSqlQuery::InitCon());
    17271898    if (query.isConnected())
    17281899    {
    1729         query.prepare("SELECT keylist FROM jumppoints WHERE "
     1900        query.prepare("SELECT keylist, multikey FROM jumppoints WHERE "
    17301901                      "destination = :DEST and hostname = :HOST ;");
    17311902        query.bindValue(":DEST", destination);
    17321903        query.bindValue(":HOST", GetMythDB()->GetHostName());
    void MythMainWindow::RegisterJump(const QString &destination, 
    17341905        if (query.exec() && query.next())
    17351906        {
    17361907            keybind = query.value(0).toString();
     1908            multikeybind = query.value(1).toString();
    17371909        }
    17381910        else
    17391911        {
    17401912            QString inskey = keybind;
     1913            QString insmultikey = multikeybind;
    17411914
    17421915            query.prepare("INSERT INTO jumppoints (destination, description, "
    1743                           "keylist, hostname) VALUES ( :DEST, :DESC, :KEYLIST, "
    1744                           ":HOST );");
     1916                          "keylist, hostname, multikey) VALUES ( :DEST, :DESC, :KEYLIST, "
     1917                          ":HOST, :MULTIKEY );");
    17451918            query.bindValue(":DEST", destination);
    17461919            query.bindValue(":DESC", description);
    17471920            query.bindValue(":KEYLIST", inskey);
    17481921            query.bindValue(":HOST", GetMythDB()->GetHostName());
     1922            query.bindValue(":MULTIKEY", insmultikey);
    17491923
    17501924            if (!query.exec() || !query.isActive())
    17511925            {
    void MythMainWindow::RegisterJump(const QString &destination, 
    17581932        { callback, destination, description, exittomain, localAction };
    17591933    d->destinationMap[destination] = jd;
    17601934
    1761     BindJump(destination, keybind);
     1935    BindJump(destination, keybind, multikeybind);
    17621936}
    17631937
    17641938void MythMainWindow::JumpTo(const QString& destination, bool pop)
  • mythtv/libs/libmythui/mythmainwindow.h

    diff --git a/mythtv/libs/libmythui/mythmainwindow.h b/mythtv/libs/libmythui/mythmainwindow.h
    index 2576a3d..2c4ac36 100644
    a b class MythPainterWindowVDPAU; 
    2828class MythPainterWindowD3D9;
    2929class MythRender;
    3030
     31class MultikeyContext
     32{
     33 public:
     34    enum MultikeyStatus {
     35        kMKnoPrefix,
     36        kMKpartialMatch,
     37        kMKfullMatch,
     38        kMKbadMatch
     39    };
     40    MultikeyContext(void)
     41        : oldPrefix(""), currentPrefix(""), status(kMKnoPrefix) {}
     42    void SetStatus(MultikeyStatus why) { status = why; }
     43    const QString &GetPrefix(void) { return currentPrefix; }
     44    bool AddKey(int key);
     45    void Commit(MultikeyStatus &s, QString &prefix) {
     46        prefix = currentPrefix;
     47        s = status;
     48        if (status != kMKpartialMatch)
     49        {
     50            currentPrefix = "";
     51            status = kMKnoPrefix;
     52        }
     53        oldPrefix = currentPrefix;
     54    }
     55 private:
     56    QString oldPrefix;
     57    QString currentPrefix;
     58    MultikeyStatus status;
     59};
     60
    3161class MUI_PUBLIC MythMainWindow : public QWidget
    3262{
    3363    Q_OBJECT
    class MUI_PUBLIC MythMainWindow : public QWidget 
    4979    MythScreenStack *GetStackAt(int pos);
    5080
    5181    bool TranslateKeyPress(const QString &context, QKeyEvent *e,
    52                            QStringList &actions, bool allowJumps = true)
     82                           QStringList &actions, bool allowJumps = true,
     83                           MultikeyContext *prefixContext = NULL)
    5384                           MUNUSED_RESULT;
    5485
    5586    void ResetKeys(void);
    5687    void ClearKey(const QString &context, const QString &action);
    5788    void ClearKeyContext(const QString &context);
    5889    void BindKey(const QString &context, const QString &action,
    59                  const QString &key);
     90                 const QString &key, const QString &multikey);
    6091    void RegisterKey(const QString &context, const QString &action,
    6192                     const QString &description, const QString &key);
    6293    QString GetKey(const QString &context, const QString &action) const;
    6394
    6495    void ClearJump(const QString &destination);
    65     void BindJump(const QString &destination, const QString &key);
     96    void BindJump(const QString &destination, const QString &key,
     97                  const QString &multikey);
    6698    void RegisterJump(const QString &destination, const QString &description,
    6799                      const QString &key, void (*callback)(void),
    68100                      bool exittomain = true, QString localAction = "");
  • mythtv/programs/mythfrontend/keybindings.cpp

    diff --git a/mythtv/programs/mythfrontend/keybindings.cpp b/mythtv/programs/mythfrontend/keybindings.cpp
    index e15de26..d099923 100644
    a b void KeyBindings::CommitAction(const ActionID &id) 
    283283    }
    284284
    285285    GetMythMainWindow()->ClearKey(id.GetContext(), id.GetAction());
    286     GetMythMainWindow()->BindKey(id.GetContext(), id.GetAction(), keys);
     286    GetMythMainWindow()->BindKey(id.GetContext(), id.GetAction(), keys, "");
    287287}
    288288
    289289/** \fn KeyBindings::CommitJumppoint(const ActionID&)
    void KeyBindings::CommitJumppoint(const ActionID &id) 
    312312    }
    313313
    314314    GetMythMainWindow()->ClearJump(id.GetAction());
    315     GetMythMainWindow()->BindJump(id.GetAction(), keys);
     315    // XXX- The following line causes the jumppoint editor to erase
     316    // from memory (but not from the database) any existing multikey
     317    // binding for the jumppoint.  Fixing this would require further
     318    // digging into action{,set}.{h,cpp}.
     319    GetMythMainWindow()->BindJump(id.GetAction(), keys, "");
    316320}
    317321
    318322/** \fn KeyBindings::CommitChanges(void)