Ticket #9613: multikeybindings_v7.patch

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

Change VERBOSE() to LOG().

  • mythtv/libs/libmythtv/dbcheck.cpp

    diff --git a/mythtv/libs/libmythtv/dbcheck.cpp b/mythtv/libs/libmythtv/dbcheck.cpp
    index 47e368a..2314eb2 100644
    a b tmp.constData(), 
    59625962"  description varchar(255) default NULL,"
    59635963"  keylist varchar(128) default NULL,"
    59645964"  hostname varchar(64) NOT NULL default '',"
     5965"  multikey varchar(128) default NULL,"
    59655966"  PRIMARY KEY  (destination,hostname)"
    59665967");",
    59675968"CREATE TABLE keybindings ("
    tmp.constData(), 
    59705971"  description varchar(255) default NULL,"
    59715972"  keylist varchar(128) default NULL,"
    59725973"  hostname varchar(64) NOT NULL default '',"
     5974"  multikey varchar(128) default NULL,"
    59735975"  PRIMARY KEY  (`context`,`action`,hostname)"
    59745976");",
    59755977"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 b2ba709..e118ca5 100644
    a b TV::TV(void) 
    874874      endOfPlaybackTimerId(0),      embedCheckTimerId(0),
    875875      endOfRecPromptTimerId(0),     videoExitDialogTimerId(0),
    876876      pseudoChangeChanTimerId(0),   speedChangeTimerId(0),
    877       errorRecoveryTimerId(0),      exitPlayerTimerId(0)
     877      errorRecoveryTimerId(0),      exitPlayerTimerId(0),
     878      multikeyContext(new MultikeyContext())
    878879{
    879880    memset(&ddMapLoader, 0, sizeof(pthread_t));
    880881    LOG(VB_GENERAL, LOG_INFO, LOC + "Creating TV object");
    881882    ctorTime.start();
    TV::~TV(void) 
    12131214    }
    12141215    ReturnPlayerLock(mctx);
    12151216
     1217    if (multikeyContext)
     1218    {
     1219        delete multikeyContext;
     1220        multikeyContext = NULL;
     1221    }
     1222
    12161223    LOG(VB_PLAYBACK, LOG_INFO, "TV::~TV() -- end");
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    32993306    if (ignoreKeys)
    33003307    {
    33013308        handled = GetMythMainWindow()->TranslateKeyPress(
    3302                   "TV Playback", e, actions);
     3309                  "TV Playback", e, actions, true, multikeyContext);
    33033310
    33043311        if (handled || actions.isEmpty())
     3312        {
     3313            CommitMultikey(actx);
    33053314            return true;
     3315        }
    33063316
    33073317        bool esc   = has_action("ESCAPE", actions) ||
    33083318                     has_action("BACK", actions);
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    33103320        bool play  = has_action(ACTION_PLAY,  actions);
    33113321
    33123322        if ((!esc || browsehelper->IsBrowsing()) && !pause && !play)
     3323        {
     3324            CommitMultikey(actx);
    33133325            return false;
     3326        }
    33143327    }
    33153328
    33163329    OSD *osd = GetOSDLock(actx);
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    33243337    if (editmode && !handled)
    33253338    {
    33263339        handled |= GetMythMainWindow()->TranslateKeyPress(
    3327                    "TV Editing", e, actions);
     3340                   "TV Editing", e, actions, true, multikeyContext);
    33283341
    33293342        if (!handled)
    33303343        {
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    33683381    }
    33693382
    33703383    if (handled)
     3384    {
     3385        CommitMultikey(actx);
    33713386        return true;
     3387    }
    33723388
    33733389    // If text is already queued up, be more lax on what is ok.
    33743390    // This allows hex teletext entry and minor channel entry.
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    33803396        if (ok || txt=="_" || txt=="-" || txt=="#" || txt==".")
    33813397        {
    33823398            AddKeyToInputQueue(actx, txt.at(0).toLatin1());
     3399            CommitMultikey(actx, false);
    33833400            return true;
    33843401        }
    33853402    }
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    33903407    {
    33913408        QStringList tt_actions;
    33923409        handled = GetMythMainWindow()->TranslateKeyPress(
    3393                   "Teletext Menu", e, tt_actions);
     3410                  "Teletext Menu", e, tt_actions, true, multikeyContext);
    33943411
    33953412        if (!handled && !tt_actions.isEmpty())
    33963413        {
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    33993416                if (actx->player->HandleTeletextAction(tt_actions[i]))
    34003417                {
    34013418                    actx->UnlockDeletePlayer(__FILE__, __LINE__);
     3419                    CommitMultikey(actx);
    34023420                    return true;
    34033421                }
    34043422            }
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    34103428    {
    34113429        QStringList itv_actions;
    34123430        handled = GetMythMainWindow()->TranslateKeyPress(
    3413                   "TV Playback", e, itv_actions);
     3431                  "TV Playback", e, itv_actions, true, multikeyContext);
    34143432
    34153433        if (!handled && !itv_actions.isEmpty())
    34163434        {
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    34193437                if (actx->player->ITVHandleAction(itv_actions[i]))
    34203438                {
    34213439                    actx->UnlockDeletePlayer(__FILE__, __LINE__);
     3440                    CommitMultikey(actx);
    34223441                    return true;
    34233442                }
    34243443            }
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    34273446    actx->UnlockDeletePlayer(__FILE__, __LINE__);
    34283447
    34293448    handled = GetMythMainWindow()->TranslateKeyPress(
    3430               "TV Playback", e, actions);
     3449              "TV Playback", e, actions, true, multikeyContext);
    34313450
    34323451    if (handled || actions.isEmpty())
     3452    {
     3453        CommitMultikey(actx);
    34333454        return true;
     3455    }
    34343456
    34353457    handled = false;
    34363458
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    34573479#endif // DEBUG_ACTIONS
    34583480
    34593481    if (handled)
     3482    {
     3483        CommitMultikey(actx);
    34603484        return true;
     3485    }
    34613486
    34623487    if (!handled)
    34633488    {
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    34713496            {
    34723497                AddKeyToInputQueue(actx, '0' + val);
    34733498                handled = true;
     3499                CommitMultikey(actx, false);
    34743500            }
    34753501        }
    34763502    }
    bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 
    34783504    return true;
    34793505}
    34803506
     3507void TV::CommitMultikey(PlayerContext *ctx, bool updateOSD)
     3508{
     3509    MultikeyContext::MultikeyStatus status;
     3510    QString prefix;
     3511    multikeyContext->Commit(status, prefix);
     3512    bool isMultikey = (QKeySequence(prefix).count() > 1);
     3513    if (updateOSD)
     3514    {
     3515        switch (status)
     3516        {
     3517        case MultikeyContext::kMKnoPrefix:
     3518            // clear the OSD text
     3519            HideOSDWindow(ctx, "osd_input");
     3520            break;
     3521        case MultikeyContext::kMKpartialMatch:
     3522            SetOSDText(ctx, "osd_input", "osd_number_entry",
     3523                       prefix, kOSDTimeout_Med);
     3524            break;
     3525        case MultikeyContext::kMKfullMatch:
     3526            // same as partial match but with short timeout
     3527            if (isMultikey)
     3528                SetOSDText(ctx, "osd_input", "osd_number_entry",
     3529                           prefix, kOSDTimeout_Short);
     3530            break;
     3531        case MultikeyContext::kMKbadMatch:
     3532            // same as full match but with error text
     3533            SetOSDText(ctx, "osd_input", "osd_number_entry",
     3534                       prefix + ": " + QObject::tr("Unknown key sequence"),
     3535                       kOSDTimeout_Short);
     3536            break;
     3537        }
     3538    }
     3539}
     3540
    34813541bool TV::BrowseHandleAction(PlayerContext *ctx, const QStringList &actions)
    34823542{
    34833543    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 9739519..83f51f7 100644
    a b using namespace std; 
    4141
    4242class QDateTime;
    4343class OSD;
     44class MultikeyContext;
    4445class RemoteEncoder;
    4546class MythPlayer;
    4647class DetectLetterbox;
    class MTV_PUBLIC TV : public QObject 
    386387    void ClearInputQueues(const PlayerContext*, bool hideosd);
    387388    bool CommitQueuedInput(PlayerContext*);
    388389    bool ProcessSmartChannel(const PlayerContext*, QString&);
     390    void CommitMultikey(PlayerContext*, bool updateOSD=true);
    389391
    390392    // query key queues
    391393    bool HasQueuedInput(void) const
    class MTV_PUBLIC TV : public QObject 
    842844    TimerContextMap      signalMonitorTimerId;
    843845    TimerContextMap      tvchainUpdateTimerId;
    844846
     847    MultikeyContext     *multikeyContext;
     848
    845849  public:
    846850    // Constants
    847851    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 7ecb158..6b4d66b 100644
    a b using namespace std; 
    8686#define LOC_WARN QString("MythMainWindow, Warning: ")
    8787#define LOC_ERR  QString("MythMainWindow, Error: ")
    8888
     89class MultikeyData
     90{
     91public:
     92    void AddMultikeyMapping(const QString &multikey, QString action)
     93    {
     94        QKeySequence seq(multikey);
     95        switch (seq.count())
     96        {
     97        case 4:
     98            ++multikeyPrefixes[QKeySequence(seq[0], seq[1], seq[2])];
     99            // fallthrough
     100        case 3:
     101            ++multikeyPrefixes[QKeySequence(seq[0], seq[1])];
     102            // fallthrough
     103        case 2:
     104            ++multikeyPrefixes[QKeySequence(seq[0])];
     105            // fallthrough
     106        case 1:
     107            multikeyActionMap[seq].append(action);
     108        }
     109    }
     110
     111    bool IsMultikeyPrefix(const QString &multikey)
     112    {
     113        return multikeyPrefixes.count(QKeySequence(multikey));
     114    }
     115
     116    bool GetMultikeyMapping(const QString &multikey, QStringList &actions)
     117    {
     118        QKeySequence seq(multikey);
     119        if (multikeyActionMap.count(seq) > 0)
     120        {
     121            actions += multikeyActionMap[seq];
     122            return true;
     123        }
     124        return false;
     125    }
     126
     127private:
     128    QMap<QKeySequence, QStringList> multikeyActionMap;
     129    QMap<QKeySequence, int> multikeyPrefixes;
     130};
     131
    89132class KeyContext
    90133{
    91134  public:
    92135    void AddMapping(int key, QString action)
    93136    {
    94137        actionMap[key].append(action);
     138        multiData.AddMultikeyMapping(QKeySequence(key).toString(), action);
    95139    }
    96140
    97141    bool GetMapping(int key, QStringList &actions)
    class KeyContext 
    105149    }
    106150
    107151    QMap<int, QStringList> actionMap;
     152    MultikeyData multiData;
    108153};
    109154
    110155struct JumpData
    class MythMainWindowPrivate 
    216261    QHash<QString, KeyContext *> keyContexts;
    217262    QMap<int, JumpData*> jumpMap;
    218263    QMap<QString, JumpData> destinationMap;
     264    MultikeyData jumpMultikeyData;
    219265    QMap<QString, MPData> mediaPluginMap;
    220266
    221267    void (*exitmenucallback)(void);
    class MythMainWindowPrivate 
    264310    bool m_pendingUpdate;
    265311};
    266312
     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
    267338// Make keynum in QKeyEvent be equivalent to what's in QKeySequence
    268339int MythMainWindowPrivate::TranslateKeyNum(QKeyEvent* e)
    269340{
    void MythMainWindow::ExitToMainMenu(void) 
    14401511 * \param e       The keypress event to lookup.
    14411512 * \param actions The QStringList that will contain the list of actions.
    14421513 * \param allowJumps if true then jump points are allowed
     1514 * \param prefixContext if non-NULL then multi-key bindings are allowed.
    14431515 *
    14441516 * \return true if the key event has been handled (the keypress was a jumpoint)
    14451517           false if the caller should continue to handle keypress
    14461518 */
    14471519bool MythMainWindow::TranslateKeyPress(const QString &context,
    14481520                                       QKeyEvent *e, QStringList &actions,
    1449                                        bool allowJumps)
     1521                                       bool allowJumps,
     1522                                       MultikeyContext *prefixContext)
    14501523{
    14511524    actions.clear();
    14521525
    bool MythMainWindow::TranslateKeyPress(const QString &context, 
    14601533    }
    14611534
    14621535    int keynum = d->TranslateKeyNum(e);
    1463 
    1464     QStringList localActions;
    1465     if (allowJumps && (d->jumpMap.count(keynum) > 0) &&
    1466         (!d->jumpMap[keynum]->localAction.isEmpty()) &&
    1467         (d->keyContexts.value(context)) &&
    1468         (d->keyContexts.value(context)->GetMapping(keynum, localActions)))
    1469     {
    1470         if (localActions.contains(d->jumpMap[keynum]->localAction))
    1471             allowJumps = false;
     1536    bool checkOnlyJumpPoints = false;
     1537    bool wasEmpty = prefixContext ? prefixContext->GetPrefix().isEmpty() : true;
     1538    bool noOverflow = prefixContext ? prefixContext->AddKey(keynum) : true;
     1539    const QString singleKey = QKeySequence(keynum).toString();
     1540    const QString prefix = prefixContext ? prefixContext->GetPrefix() : singleKey;
     1541    MultikeyContext::MultikeyStatus status;
     1542    JumpData *singleKeyJump = NULL, *multiKeyJump = NULL;
     1543        // There is a potential race condition where an input timeout
     1544        // thread resets the context at the same time this code is
     1545        // operating on it.
     1546    QStringList singleKeyJumpActions, multiKeyJumpActions;
     1547    QStringList singleKeyActions, multiKeyActions;
     1548    KeyContext *kctx = d->keyContexts.value(context);
     1549    KeyContext *gctx = d->keyContexts.value("Global");
     1550
     1551    if (allowJumps)
     1552    {
     1553        // Find single-key jumppoint bindings.  This is only used when
     1554        // a multi-key sequence (regular or jumppoint) is aborted
     1555        // mid-sequence with a single-key jump point.
     1556        if (d->jumpMultikeyData.GetMultikeyMapping(singleKey, singleKeyJumpActions))
     1557            singleKeyJump = &d->destinationMap[singleKeyJumpActions[0]];
     1558        // Find multi-key jumppoint bindings.
     1559        if (noOverflow && d->jumpMultikeyData.GetMultikeyMapping(prefix, multiKeyJumpActions))
     1560            multiKeyJump = &d->destinationMap[multiKeyJumpActions[0]];
     1561    }
     1562    // Find regular bindings (both single- and multi-key) in the given
     1563    // and global contexts.
     1564    if (kctx)
     1565    {
     1566        if (noOverflow)
     1567            kctx->multiData.GetMultikeyMapping(prefix, multiKeyActions);
     1568        kctx->multiData.GetMultikeyMapping(singleKey, singleKeyActions);
     1569    }
     1570    if (gctx && context != "Global")
     1571    {
     1572        if (noOverflow)
     1573            gctx->multiData.GetMultikeyMapping(prefix, multiKeyActions);
     1574        gctx->multiData.GetMultikeyMapping(singleKey, singleKeyActions);
     1575    }
     1576
     1577    // Disable the jump point if there is a corresponding local action
     1578    // for the key sequence.
     1579    if (multiKeyJump &&
     1580        !multiKeyJump->localAction.isEmpty() &&
     1581        multiKeyActions.contains(multiKeyJump->localAction))
     1582    {
     1583        multiKeyJump = singleKeyJump = NULL;
     1584    }
     1585    if (singleKeyJump &&
     1586        !singleKeyJump->localAction.isEmpty() &&
     1587        singleKeyActions.contains(singleKeyJump->localAction))
     1588    {
     1589        multiKeyJump = singleKeyJump = NULL;
     1590        multiKeyActions = singleKeyActions;
     1591    }
     1592
     1593    // Execute the jump point if there is one.
     1594    if (multiKeyJump || (singleKeyJump && multiKeyActions.isEmpty()))
     1595    {
     1596        JumpData *jumpEntry = multiKeyJump ? multiKeyJump : singleKeyJump;
     1597        if (!jumpEntry->exittomain && d->exitmenucallback == NULL)
     1598        {
     1599            if (prefixContext)
     1600                prefixContext->SetStatus(MultikeyContext::kMKfullMatch);
     1601            void (*callback)(void) = jumpEntry->callback;
     1602            callback();
     1603            return true;
     1604        }
     1605        if (d->exitmenucallback == NULL)
     1606        {
     1607            if (prefixContext)
     1608                prefixContext->SetStatus(MultikeyContext::kMKfullMatch);
     1609            d->exitingtomain = true;
     1610            d->exitmenucallback = jumpEntry->callback;
     1611            QCoreApplication::postEvent(
     1612                this, new QEvent(MythEvent::kExitToMainMenuEventType));
     1613            return true;
     1614        }
    14721615    }
    14731616
    1474     if (allowJumps && d->jumpMap.count(keynum) > 0 &&
    1475             !d->jumpMap[keynum]->exittomain && d->exitmenucallback == NULL)
     1617    // Handle a regular action if there is one.
     1618    if (!multiKeyActions.isEmpty())
    14761619    {
    1477         void (*callback)(void) = d->jumpMap[keynum]->callback;
    1478         callback();
    1479         return true;
     1620        if (prefixContext)
     1621            prefixContext->SetStatus(MultikeyContext::kMKfullMatch);
     1622        actions = multiKeyActions;
     1623        return false;
    14801624    }
    14811625
    1482     if (allowJumps &&
    1483         d->jumpMap.count(keynum) > 0 && d->exitmenucallback == NULL)
     1626    // No full match, so handle partial matches and bad matches.
     1627    if (noOverflow)
    14841628    {
    1485         d->exitingtomain = true;
    1486         d->exitmenucallback = d->jumpMap[keynum]->callback;
    1487         QCoreApplication::postEvent(
    1488             this, new QEvent(MythEvent::kExitToMainMenuEventType));
    1489         return true;
     1629        // Check for a partial match.
     1630        if ((kctx && kctx->multiData.IsMultikeyPrefix(prefix)) ||
     1631            (gctx && context != "Global" && gctx->multiData.IsMultikeyPrefix(prefix)) ||
     1632            (d->jumpMultikeyData.IsMultikeyPrefix(prefix)))
     1633        {
     1634            if (prefixContext)
     1635                prefixContext->SetStatus(MultikeyContext::kMKpartialMatch);
     1636            return false;
     1637        }
    14901638    }
    1491 
    1492     if (d->keyContexts.value(context))
    1493         d->keyContexts.value(context)->GetMapping(keynum, actions);
    1494 
    1495     if (context != "Global")
    1496         d->keyContexts.value("Global")->GetMapping(keynum, actions);
    1497 
     1639    if (prefixContext)
     1640        prefixContext->SetStatus(wasEmpty ? MultikeyContext::kMKnoPrefix :
     1641                                 MultikeyContext::kMKbadMatch);
    14981642    return false;
    14991643}
    15001644
    void MythMainWindow::ClearKeyContext(const QString &context) 
    15231667}
    15241668
    15251669void MythMainWindow::BindKey(const QString &context, const QString &action,
    1526                              const QString &key)
     1670                             const QString &key, const QString &multikey)
    15271671{
    15281672    QKeySequence keyseq(key);
    15291673
    void MythMainWindow::BindKey(const QString &context, const QString &action, 
    15511695        if (action == "ESCAPE" && context == "Global" && i == 0)
    15521696            d->escapekey = keynum;
    15531697    }
     1698
     1699    QStringList multidummyaction("");
     1700    if (d->keyContexts.value(context)->multiData.GetMultikeyMapping(multikey, multidummyaction))
     1701    {
     1702        LOG(VB_GENERAL, LOG_INFO, QString("Multikey %1 is bound to multiple actions "
     1703                                    "in context %2.")
     1704                .arg(multikey).arg(context));
     1705    }
     1706    d->keyContexts.value(context)->multiData.AddMultikeyMapping(multikey, action);
    15541707}
    15551708
    15561709void MythMainWindow::RegisterKey(const QString &context, const QString &action,
    15571710                                 const QString &description, const QString &key)
    15581711{
    15591712    QString keybind = key;
     1713    QString multikeybind = "";
    15601714
    15611715    MSqlQuery query(MSqlQuery::InitCon());
    15621716
    15631717    if (d->m_useDB && query.isConnected())
    15641718    {
    1565         query.prepare("SELECT keylist, description FROM keybindings WHERE "
     1719        query.prepare("SELECT keylist, description, multikey FROM keybindings WHERE "
    15661720                      "context = :CONTEXT AND action = :ACTION AND "
    15671721                      "hostname = :HOSTNAME ;");
    15681722        query.bindValue(":CONTEXT", context);
    void MythMainWindow::RegisterKey(const QString &context, const QString &action, 
    15731727        {
    15741728            keybind = query.value(0).toString();
    15751729            QString db_description = query.value(1).toString();
     1730            multikeybind = query.value(2).toString();
    15761731
    15771732            // Update keybinding description if changed
    15781733            if (db_description != description)
    void MythMainWindow::RegisterKey(const QString &context, const QString &action, 
    15991754        else
    16001755        {
    16011756            QString inskey = keybind;
     1757            QString insmultikey = multikeybind;
    16021758
    16031759            query.prepare("INSERT INTO keybindings (context, action, "
    1604                           "description, keylist, hostname) VALUES "
     1760                          "description, keylist, hostname, multikey) VALUES "
    16051761                          "( :CONTEXT, :ACTION, :DESCRIPTION, :KEYLIST, "
    1606                           ":HOSTNAME );");
     1762                          ":HOSTNAME, :MULTIKEY );");
    16071763            query.bindValue(":CONTEXT", context);
    16081764            query.bindValue(":ACTION", action);
    16091765            query.bindValue(":DESCRIPTION", description);
    16101766            query.bindValue(":KEYLIST", inskey);
    16111767            query.bindValue(":HOSTNAME", GetMythDB()->GetHostName());
     1768            query.bindValue(":MULTIKEY", insmultikey);
    16121769
    16131770            if (!query.exec() && !(GetMythDB()->SuppressDBMessages()))
    16141771            {
    void MythMainWindow::RegisterKey(const QString &context, const QString &action, 
    16171774        }
    16181775    }
    16191776
    1620     BindKey(context, action, keybind);
     1777    BindKey(context, action, keybind, multikeybind);
    16211778}
    16221779
    16231780QString MythMainWindow::GetKey(const QString &context,
    void MythMainWindow::ClearJump(const QString &destination) 
    16621819}
    16631820
    16641821
    1665 void MythMainWindow::BindJump(const QString &destination, const QString &key)
     1822void MythMainWindow::BindJump(const QString &destination, const QString &key,
     1823                              const QString &multikey)
    16661824{
    16671825    /* make sure the jump point exists */
    16681826    if (d->destinationMap.find(destination) == d->destinationMap.end())
    void MythMainWindow::BindJump(const QString &destination, const QString &key) 
    16841842#endif
    16851843
    16861844            d->jumpMap[keynum] = &d->destinationMap[destination];
     1845            d->jumpMultikeyData.AddMultikeyMapping(QKeySequence(keynum).toString(), destination);
    16871846        }
    16881847        else
    16891848        {
    void MythMainWindow::BindJump(const QString &destination, const QString &key) 
    16951854        LOG(VB_GENERAL, LOG_DEBUG,
    16961855            QString("JumpPoint: %2 exists, no keybinding") .arg(destination));
    16971856#endif
     1857
     1858    d->jumpMultikeyData.AddMultikeyMapping(multikey, destination);
    16981859}
    16991860
    17001861void MythMainWindow::RegisterJump(const QString &destination,
    void MythMainWindow::RegisterJump(const QString &destination, 
    17031863                                  bool exittomain, QString localAction)
    17041864{
    17051865    QString keybind = key;
     1866    QString multikeybind = "";
    17061867
    17071868    MSqlQuery query(MSqlQuery::InitCon());
    17081869    if (query.isConnected())
    17091870    {
    1710         query.prepare("SELECT keylist FROM jumppoints WHERE "
     1871        query.prepare("SELECT keylist, multikey FROM jumppoints WHERE "
    17111872                      "destination = :DEST and hostname = :HOST ;");
    17121873        query.bindValue(":DEST", destination);
    17131874        query.bindValue(":HOST", GetMythDB()->GetHostName());
    void MythMainWindow::RegisterJump(const QString &destination, 
    17151876        if (query.exec() && query.next())
    17161877        {
    17171878            keybind = query.value(0).toString();
     1879            multikeybind = query.value(1).toString();
    17181880        }
    17191881        else
    17201882        {
    17211883            QString inskey = keybind;
     1884            QString insmultikey = multikeybind;
    17221885
    17231886            query.prepare("INSERT INTO jumppoints (destination, description, "
    1724                           "keylist, hostname) VALUES ( :DEST, :DESC, :KEYLIST, "
    1725                           ":HOST );");
     1887                          "keylist, hostname, multikey) VALUES ( :DEST, :DESC, :KEYLIST, "
     1888                          ":HOST, :MULTIKEY );");
    17261889            query.bindValue(":DEST", destination);
    17271890            query.bindValue(":DESC", description);
    17281891            query.bindValue(":KEYLIST", inskey);
    17291892            query.bindValue(":HOST", GetMythDB()->GetHostName());
     1893            query.bindValue(":MULTIKEY", insmultikey);
    17301894
    17311895            if (!query.exec() || !query.isActive())
    17321896            {
    void MythMainWindow::RegisterJump(const QString &destination, 
    17391903        { callback, destination, description, exittomain, localAction };
    17401904    d->destinationMap[destination] = jd;
    17411905
    1742     BindJump(destination, keybind);
     1906    BindJump(destination, keybind, multikeybind);
    17431907}
    17441908
    17451909void 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 c007067..566d1c0 100644
    a b class MythPainterWindowVDPAU; 
    2727class MythPainterWindowD3D9;
    2828class MythRender;
    2929
     30class MultikeyContext
     31{
     32 public:
     33    enum MultikeyStatus {
     34        kMKnoPrefix,
     35        kMKpartialMatch,
     36        kMKfullMatch,
     37        kMKbadMatch
     38    };
     39    MultikeyContext(void)
     40        : oldPrefix(""), currentPrefix(""), status(kMKnoPrefix) {}
     41    void SetStatus(MultikeyStatus why) { status = why; }
     42    const QString &GetPrefix(void) { return currentPrefix; }
     43    bool AddKey(int key);
     44    void Commit(MultikeyStatus &s, QString &prefix) {
     45        prefix = currentPrefix;
     46        s = status;
     47        if (status != kMKpartialMatch)
     48        {
     49            currentPrefix = "";
     50            status = kMKnoPrefix;
     51        }
     52        oldPrefix = currentPrefix;
     53    }
     54 private:
     55    QString oldPrefix;
     56    QString currentPrefix;
     57    MultikeyStatus status;
     58};
     59
    3060class MUI_PUBLIC MythMainWindow : public QWidget
    3161{
    3262    Q_OBJECT
    class MUI_PUBLIC MythMainWindow : public QWidget 
    4878    MythScreenStack *GetStackAt(int pos);
    4979
    5080    bool TranslateKeyPress(const QString &context, QKeyEvent *e,
    51                            QStringList &actions, bool allowJumps = true)
     81                           QStringList &actions, bool allowJumps = true,
     82                           MultikeyContext *prefixContext = NULL)
    5283                           MUNUSED_RESULT;
    5384
    5485    void ResetKeys(void);
    5586    void ClearKey(const QString &context, const QString &action);
    5687    void ClearKeyContext(const QString &context);
    5788    void BindKey(const QString &context, const QString &action,
    58                  const QString &key);
     89                 const QString &key, const QString &multikey);
    5990    void RegisterKey(const QString &context, const QString &action,
    6091                     const QString &description, const QString &key);
    6192    QString GetKey(const QString &context, const QString &action) const;
    6293
    6394    void ClearJump(const QString &destination);
    64     void BindJump(const QString &destination, const QString &key);
     95    void BindJump(const QString &destination, const QString &key,
     96                  const QString &multikey);
    6597    void RegisterJump(const QString &destination, const QString &description,
    6698                      const QString &key, void (*callback)(void),
    6799                      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)